diff --git a/trunk/3rdparty/srs-bench/gb28181/h265reader.go b/trunk/3rdparty/srs-bench/gb28181/h265reader.go index 9073c5c32..d90cb0bbf 100644 --- a/trunk/3rdparty/srs-bench/gb28181/h265reader.go +++ b/trunk/3rdparty/srs-bench/gb28181/h265reader.go @@ -1,226 +1,50 @@ -// Package h265reader implements a H265 Annex-B Reader +// Package gb28181 provides GB28181 protocol support package gb28181 import ( - "bytes" - "errors" "io" + + "github.com/pion/webrtc/v4/pkg/media/h265reader" ) -type NalUnitType uint8 +// Type aliases for compatibility with existing code +type H265Reader = h265reader.H265Reader +type NAL = h265reader.NAL +type NalUnitType = h265reader.NalUnitType -// Enums for NalUnitTypes +// NAL unit type constants for compatibility with existing code const ( - NaluTypeSliceTrailN NalUnitType = 0 // 0x0 - NaluTypeSliceTrailR NalUnitType = 1 // 0x01 - NaluTypeSliceTsaN NalUnitType = 2 // 0x02 - NaluTypeSliceTsaR NalUnitType = 3 // 0x03 - NaluTypeSliceStsaN NalUnitType = 4 // 0x04 - NaluTypeSliceStsaR NalUnitType = 5 // 0x05 - NaluTypeSliceRadlN NalUnitType = 6 // 0x06 - NaluTypeSliceRadlR NalUnitType = 7 // 0x07 - NaluTypeSliceRaslN NalUnitType = 8 // 0x06 - NaluTypeSliceRaslR NalUnitType = 9 // 0x09 + NaluTypeSliceTrailN = h265reader.NalUnitTypeTrailN + NaluTypeSliceTrailR = h265reader.NalUnitTypeTrailR + NaluTypeSliceTsaN = h265reader.NalUnitTypeTsaN + NaluTypeSliceTsaR = h265reader.NalUnitTypeTsaR + NaluTypeSliceStsaN = h265reader.NalUnitTypeStsaN + NaluTypeSliceStsaR = h265reader.NalUnitTypeStsaR + NaluTypeSliceRadlN = h265reader.NalUnitTypeRadlN + NaluTypeSliceRadlR = h265reader.NalUnitTypeRadlR + NaluTypeSliceRaslN = h265reader.NalUnitTypeRaslN + NaluTypeSliceRaslR = h265reader.NalUnitTypeRaslR - NaluTypeSliceBlaWlp NalUnitType = 16 // 0x10 - NaluTypeSliceBlaWradl NalUnitType = 17 // 0x11 - NaluTypeSliceBlaNlp NalUnitType = 18 // 0x12 - NaluTypeSliceIdr NalUnitType = 19 // 0x13 - NaluTypeSliceIdrNlp NalUnitType = 20 // 0x14 - NaluTypeSliceCranut NalUnitType = 21 // 0x15 - NaluTypeSliceRsvIrapVcl22 NalUnitType = 22 // 0x16 - NaluTypeSliceRsvIrapVcl23 NalUnitType = 23 // 0x17 + NaluTypeSliceBlaWlp = h265reader.NalUnitTypeBlaWLp + NaluTypeSliceBlaWradl = h265reader.NalUnitTypeBlaWRadl + NaluTypeSliceBlaNlp = h265reader.NalUnitTypeBlaNLp + NaluTypeSliceIdr = h265reader.NalUnitTypeIdrWRadl + NaluTypeSliceIdrNlp = h265reader.NalUnitTypeIdrNLp + NaluTypeSliceCranut = h265reader.NalUnitTypeCraNut + NaluTypeSliceRsvIrapVcl22 = h265reader.NalUnitTypeReserved41 // Approximate mapping + NaluTypeSliceRsvIrapVcl23 = h265reader.NalUnitTypeReserved47 // Approximate mapping - NaluTypeVps NalUnitType = 32 // 0x20 - NaluTypeSps NalUnitType = 33 // 0x21 - NaluTypePps NalUnitType = 34 // 0x22 - NaluTypeAud NalUnitType = 35 // 0x23 - NaluTypeSei NalUnitType = 39 // 0x27 - NaluTypeSeiSuffix NalUnitType = 40 // 0x28 + NaluTypeVps = h265reader.NalUnitTypeVps + NaluTypeSps = h265reader.NalUnitTypeSps + NaluTypePps = h265reader.NalUnitTypePps + NaluTypeAud = h265reader.NalUnitTypeAud + NaluTypeSei = h265reader.NalUnitTypePrefixSei + NaluTypeSeiSuffix = h265reader.NalUnitTypeSuffixSei - NaluTypeUnspecified NalUnitType = 48 // 0x30 + NaluTypeUnspecified = h265reader.NalUnitTypeUnspec48 ) -// H265Reader reads data from stream and constructs h265 nal units -type H265Reader struct { - stream io.Reader - nalBuffer []byte - countOfConsecutiveZeroBytes int - nalPrefixParsed bool - readBuffer []byte -} - -var ( - errNilReader = errors.New("stream is nil") - errDataIsNotH265Stream = errors.New("data is not a H265 bitstream") -) - -// NewReader creates new H265Reader +// NewReader creates new H265Reader using Pion's implementation func NewReader(in io.Reader) (*H265Reader, error) { - if in == nil { - return nil, errNilReader - } - - reader := &H265Reader{ - stream: in, - nalBuffer: make([]byte, 0), - nalPrefixParsed: false, - readBuffer: make([]byte, 0), - } - - return reader, nil -} - -// NAL H.265 Network Abstraction Layer -type NAL struct { - PictureOrderCount uint32 - - // NAL header - ForbiddenZeroBit bool - UnitType NalUnitType - NuhLayerId uint8 - NuhTemporalIdPlus1 uint8 - - Data []byte // header byte + rbsp -} - -func (reader *H265Reader) read(numToRead int) (data []byte) { - for len(reader.readBuffer) < numToRead { - buf := make([]byte, 4096) - n, err := reader.stream.Read(buf) - if n == 0 || err != nil { - break - } - buf = buf[0:n] - reader.readBuffer = append(reader.readBuffer, buf...) - } - var numShouldRead int - if numToRead <= len(reader.readBuffer) { - numShouldRead = numToRead - } else { - numShouldRead = len(reader.readBuffer) - } - data = reader.readBuffer[0:numShouldRead] - reader.readBuffer = reader.readBuffer[numShouldRead:] - return data -} - -func (reader *H265Reader) bitStreamStartsWithH265Prefix() (prefixLength int, e error) { - nalPrefix3Bytes := []byte{0, 0, 1} - nalPrefix4Bytes := []byte{0, 0, 0, 1} - - prefixBuffer := reader.read(4) - - n := len(prefixBuffer) - - if n == 0 { - return 0, io.EOF - } - - if n < 3 { - return 0, errDataIsNotH265Stream - } - - nalPrefix3BytesFound := bytes.Equal(nalPrefix3Bytes, prefixBuffer[:3]) - if n == 3 { - if nalPrefix3BytesFound { - return 0, io.EOF - } - return 0, errDataIsNotH265Stream - } - - // n == 4 - if nalPrefix3BytesFound { - reader.nalBuffer = append(reader.nalBuffer, prefixBuffer[3]) - return 3, nil - } - - nalPrefix4BytesFound := bytes.Equal(nalPrefix4Bytes, prefixBuffer) - if nalPrefix4BytesFound { - return 4, nil - } - return 0, errDataIsNotH265Stream -} - -// NextNAL reads from stream and returns then next NAL, -// and an error if there is incomplete frame data. -// Returns all nil values when no more NALs are available. -func (reader *H265Reader) NextNAL() (*NAL, error) { - if !reader.nalPrefixParsed { - _, err := reader.bitStreamStartsWithH265Prefix() - if err != nil { - return nil, err - } - - reader.nalPrefixParsed = true - } - - for { - buffer := reader.read(1) - n := len(buffer) - - if n != 1 { - break - } - readByte := buffer[0] - nalFound := reader.processByte(readByte) - if nalFound { - nal := newNal(reader.nalBuffer) - nal.parseHeader() - if nal.UnitType == NaluTypeSeiSuffix || nal.UnitType == NaluTypeSei { - reader.nalBuffer = nil - continue - } else { - break - } - } - - reader.nalBuffer = append(reader.nalBuffer, readByte) - } - - if len(reader.nalBuffer) == 0 { - return nil, io.EOF - } - - nal := newNal(reader.nalBuffer) - reader.nalBuffer = nil - nal.parseHeader() - - return nal, nil -} - -func (reader *H265Reader) processByte(readByte byte) (nalFound bool) { - nalFound = false - - switch readByte { - case 0: - reader.countOfConsecutiveZeroBytes++ - case 1: - if reader.countOfConsecutiveZeroBytes >= 2 { - countOfConsecutiveZeroBytesInPrefix := 2 - if reader.countOfConsecutiveZeroBytes > 2 { - countOfConsecutiveZeroBytesInPrefix = 3 - } - nalUnitLength := len(reader.nalBuffer) - countOfConsecutiveZeroBytesInPrefix - reader.nalBuffer = reader.nalBuffer[0:nalUnitLength] - reader.countOfConsecutiveZeroBytes = 0 - nalFound = true - } else { - reader.countOfConsecutiveZeroBytes = 0 - } - default: - reader.countOfConsecutiveZeroBytes = 0 - } - - return nalFound -} - -func newNal(data []byte) *NAL { - return &NAL{PictureOrderCount: 0, ForbiddenZeroBit: false, UnitType: NaluTypeUnspecified, Data: data} -} - -func (h *NAL) parseHeader() { - firstByte := h.Data[0] - h.ForbiddenZeroBit = (((firstByte & 0x80) >> 7) == 1) // 0x80 = 0b10000000 - h.UnitType = NalUnitType((firstByte & 0x7E) >> 1) // 0x1F = 0b01111110 + return h265reader.NewReader(in) } diff --git a/trunk/3rdparty/srs-bench/gb28181/ingester.go b/trunk/3rdparty/srs-bench/gb28181/ingester.go index 87d9752dc..9870607db 100644 --- a/trunk/3rdparty/srs-bench/gb28181/ingester.go +++ b/trunk/3rdparty/srs-bench/gb28181/ingester.go @@ -456,13 +456,13 @@ func (v *PSIngester) writeH265(ctx context.Context, pack *PSPackStream, h265 *H2 videoFrames = append(videoFrames, frame) logger.If(ctx, "NALU %v PictureOrderCount=%v, ForbiddenZeroBit=%v, %v bytes", - frame.UnitType, frame.PictureOrderCount, frame.ForbiddenZeroBit, len(frame.Data)) + frame.NalUnitType, frame.PictureOrderCount, frame.ForbiddenZeroBit, len(frame.Data)) - if frame.UnitType == NaluTypeVps { + if frame.NalUnitType == NaluTypeVps { vps = frame - } else if frame.UnitType == NaluTypeSps { + } else if frame.NalUnitType == NaluTypeSps { sps = frame - } else if frame.UnitType == NaluTypePps { + } else if frame.NalUnitType == NaluTypePps { pps = frame } else { break diff --git a/trunk/3rdparty/srs-bench/go.mod b/trunk/3rdparty/srs-bench/go.mod index d762e6f52..176db4483 100644 --- a/trunk/3rdparty/srs-bench/go.mod +++ b/trunk/3rdparty/srs-bench/go.mod @@ -1,6 +1,6 @@ module github.com/ossrs/srs-bench -go 1.21 +go 1.23.0 require ( github.com/ghettovoice/gosip v0.0.0-20220929080231-de8ba881be83 @@ -8,13 +8,13 @@ require ( github.com/haivision/srtgo v0.0.0-20230627061225-a70d53fcd618 github.com/ossrs/go-oryx-lib v0.0.9 github.com/pion/ice/v4 v4.0.10 - github.com/pion/interceptor v0.1.37 - github.com/pion/logging v0.2.3 + github.com/pion/interceptor v0.1.40 + github.com/pion/logging v0.2.4 github.com/pion/rtcp v1.2.15 - github.com/pion/rtp v1.8.15 - github.com/pion/sdp/v3 v3.0.11 + github.com/pion/rtp v1.8.20 + github.com/pion/sdp/v3 v3.0.14 github.com/pion/transport/v3 v3.0.7 - github.com/pion/webrtc/v4 v4.1.1 + github.com/pion/webrtc/v4 v4.1.3 github.com/pkg/errors v0.9.1 github.com/yapingcat/gomedia/codec v0.0.0-20220617074658-94762898dc25 github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25 @@ -35,16 +35,16 @@ require ( github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/sctp v1.8.39 // indirect - github.com/pion/srtp/v3 v3.0.4 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect github.com/pion/stun/v3 v3.0.0 // indirect - github.com/pion/turn/v4 v4.0.0 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect github.com/sirupsen/logrus v1.4.2 // indirect github.com/tevino/abool v0.0.0-20170917061928-9b9efcf221b5 // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect ) diff --git a/trunk/3rdparty/srs-bench/go.sum b/trunk/3rdparty/srs-bench/go.sum index 6ace3527b..0872ce64e 100644 --- a/trunk/3rdparty/srs-bench/go.sum +++ b/trunk/3rdparty/srs-bench/go.sum @@ -65,32 +65,32 @@ github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= -github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= -github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= -github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= -github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= +github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= +github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.15 h1:MuhuGn1cxpVCPLNY1lI7F1tQ8Spntpgf12ob+pOYT8s= -github.com/pion/rtp v1.8.15/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/rtp v1.8.20 h1:8zcyqohadZE8FCBeGdyEvHiclPIezcwRQH9zfapFyYI= +github.com/pion/rtp v1.8.20/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= -github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= -github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= -github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= +github.com/pion/sdp/v3 v3.0.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI= +github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= -github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= -github.com/pion/webrtc/v4 v4.1.1 h1:PMFPtLg1kpD2pVtun+LGUzA3k54JdFl87WO0Z1+HKug= -github.com/pion/webrtc/v4 v4.1.1/go.mod h1:cgEGkcpxGkT6Di2ClBYO5lP9mFXbCfEOrkYUpjjCQO4= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.3 h1:YZ67Boj9X/hk190jJZ8+HFGQ6DqSZ/fYP3sLAZv7c3c= +github.com/pion/webrtc/v4 v4.1.3/go.mod h1:rsq+zQ82ryfR9vbb0L1umPJ6Ogq7zm8mcn9fcGnxomM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -117,8 +117,8 @@ github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25/go.mod h1: golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -126,8 +126,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -144,15 +144,15 @@ golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214095126-aec9a390925b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/trunk/3rdparty/srs-bench/srs/rtmp_test.go b/trunk/3rdparty/srs-bench/srs/rtmp_test.go index 5f93671ba..9dd38da0a 100644 --- a/trunk/3rdparty/srs-bench/srs/rtmp_test.go +++ b/trunk/3rdparty/srs-bench/srs/rtmp_test.go @@ -102,7 +102,7 @@ func TestRtmpPublishPlay(t *testing.T) { } } -func TestRtmpPublish_RtcPlay(t *testing.T) { +func TestRtmpPublish_RtcPlay_AVC(t *testing.T) { ctx := logger.WithContext(context.Background()) ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond) @@ -119,10 +119,11 @@ func TestRtmpPublish_RtcPlay(t *testing.T) { return err } - // Setup the RTC player. + // Setup the RTC player with AVC codec support. var thePlayer *testPlayer if thePlayer, err = newTestPlayer(registerMiniCodecs, func(play *testPlayer) error { play.streamSuffix = streamSuffix + play.streamCodec = "h264" var nnPlayReadRTP uint64 return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) { api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) { @@ -234,7 +235,7 @@ func TestRtmpPublish_MultipleSequences(t *testing.T) { } // Ingore the duplicated sps/pps. - if IsAvccrEquals(previousAvccr, avccr) { + if isAvccrEquals(previousAvccr, avccr) { return nil } previousAvccr = avccr @@ -316,7 +317,7 @@ func TestRtmpPublish_MultipleSequences_RtcPlay(t *testing.T) { return nn, attr, err } - annexb, nalus, err := DemuxRtpSpsPps(payload[:nn]) + annexb, nalus, err := demuxRtpSpsPps(payload[:nn]) if err != nil || len(nalus) == 0 || (nalus[0].NALUType != avc.NALUTypeSPS && nalus[0].NALUType != avc.NALUTypePPS) || bytes.Equal(annexb, previousSpsPps) { @@ -640,3 +641,95 @@ func TestRtmpPublish_HttpFlvPlayNoVideo(t *testing.T) { t.Errorf("err %+v", err) } } + +// TestRtmpPublish_RtcPlay_HEVC tests HEVC support in RTMP to RTC pipeline (PR 4289) +// This test publishes H.265 video via RTMP and plays it back via WebRTC with codec=hevc +func TestRtmpPublish_RtcPlay_HEVC(t *testing.T) { + ctx := logger.WithContext(context.Background()) + ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond) + + var r0, r1 error + err := func() (err error) { + streamSuffix := fmt.Sprintf("rtmp-hevc-regression-%v-%v", os.Getpid(), rand.Int()) + rtmpUrl := fmt.Sprintf("%v://%v%v-%v", srsSchema, *srsServer, *srsStream, streamSuffix) + + // Publisher connect to a RTMP stream. + publisher := NewRTMPPublisher() + defer publisher.Close() + + if err := publisher.Publish(ctx, rtmpUrl); err != nil { + return err + } + + // Setup the RTC player with HEVC codec support. + var thePlayer *testPlayer + if thePlayer, err = newTestPlayer(registerHEVCCodecs, func(play *testPlayer) error { + play.streamSuffix = streamSuffix + play.streamCodec = "hevc" + var nnPlayReadRTP uint64 + return play.Setup(*srsVnetClientIP, func(api *testWebRTCAPI) { + api.registry.Add(newRTPInterceptor(func(i *rtpInterceptor) { + i.rtpReader = func(payload []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) { + nn, attr, err := i.nextRTPReader.Read(payload, attributes) + if err != nil { + return nn, attr, err + } + + if nnPlayReadRTP++; nnPlayReadRTP >= uint64(*srsPlayOKPackets) { + cancel() // Completed. + } + logger.Tf(ctx, "Play RECV RTP #%v %vB", nnPlayReadRTP, nn) + return nn, attr, err + } + })) + }) + }); err != nil { + return err + } + defer thePlayer.Close() + + // Run publisher and players. + var wg sync.WaitGroup + defer wg.Wait() + + var playerIceReady context.Context + playerIceReady, thePlayer.iceReadyCancel = context.WithCancel(ctx) + + wg.Add(1) + go func() { + defer wg.Done() + if r1 = thePlayer.Run(logger.WithContext(ctx), cancel); r1 != nil { + cancel() + } + logger.Tf(ctx, "player done") + }() + + wg.Add(1) + go func() { + defer wg.Done() + + // Wait for player ready. + select { + case <-ctx.Done(): + return + case <-playerIceReady.Done(): + } + + publisher.onSendPacket = func(m *rtmp.Message) error { + time.Sleep(100 * time.Microsecond) + return nil + } + // Use H.265 file directly without ffmpeg transcoding, implementing new h265 demuxer + // in RTMPPublisher using pion pkg/media/h265reader with enhanced-RTMP fourcc 'hvc1' + if r0 = publisher.Ingest(ctx, *srsPublishVideoH265); r0 != nil { + cancel() + } + logger.Tf(ctx, "publisher done") + }() + + return nil + }() + if err := filterTestError(ctx.Err(), err, r0, r1); err != nil { + t.Errorf("err %+v", err) + } +} diff --git a/trunk/3rdparty/srs-bench/srs/util.go b/trunk/3rdparty/srs-bench/srs/util.go index 7836bf701..eb60286a2 100644 --- a/trunk/3rdparty/srs-bench/srs/util.go +++ b/trunk/3rdparty/srs-bench/srs/util.go @@ -61,6 +61,7 @@ import ( "github.com/pion/transport/v3/vnet" "github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4/pkg/media/h264reader" + "github.com/pion/webrtc/v4/pkg/media/h265reader" ) var srsHttps *bool @@ -80,6 +81,7 @@ var srsStream *string var srsLiveStream *string var srsPublishAudio *string var srsPublishVideo *string +var srsPublishVideoH265 *string var srsPublishAvatar *string var srsPublishBBB *string var srsVnetClientIP *string @@ -97,6 +99,7 @@ func prepareTest() (err error) { srsPublishOKPackets = flag.Int("srs-publish-ok-packets", 3, "If send N RTP, recv N RTCP packets, it's ok, or fail") srsPublishAudio = flag.String("srs-publish-audio", "avatar.ogg", "The audio file for publisher.") srsPublishVideo = flag.String("srs-publish-video", "avatar.h264", "The video file for publisher.") + srsPublishVideoH265 = flag.String("srs-publish-video-h265", "avatar.h265", "The H.265 video file for publisher.") srsPublishAvatar = flag.String("srs-publish-avatar", "avatar.flv", "The avatar file for publisher.") srsPublishBBB = flag.String("srs-publish-bbb", "bbb.flv", "The bbb file for publisher.") srsPublishVideoFps = flag.Int("srs-publish-video-fps", 25, "The video fps for publisher.") @@ -143,6 +146,10 @@ func prepareTest() (err error) { return err } + if *srsPublishVideoH265, err = tryOpenFile(*srsPublishVideoH265); err != nil { + return err + } + if *srsPublishAvatar, err = tryOpenFile(*srsPublishAvatar); err != nil { return err } @@ -633,16 +640,28 @@ func registerMiniCodecs(api *testWebRTCAPI) error { v := api if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, - PayloadType: 111, + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, + SDPFmtpLine: "minptime=10;useinbandfec=1", RTCPFeedback: nil, + }, + PayloadType: 111, }, webrtc.RTPCodecTypeAudio); err != nil { return err } - videoRTCPFeedback := []webrtc.RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}} + videoRTCPFeedback := []webrtc.RTCPFeedback{ + {Type: "goog-remb", Parameter: ""}, + {Type: "ccm", Parameter: "fir"}, + {Type: "nack", Parameter: ""}, + {Type: "nack", Parameter: "pli"}, + } if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback}, - PayloadType: 108, + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, ClockRate: 90000, Channels: 0, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", + RTCPFeedback: videoRTCPFeedback, + }, + PayloadType: 108, }, webrtc.RTPCodecTypeVideo); err != nil { return err } @@ -656,16 +675,26 @@ func registerMiniCodecsWithoutNack(api *testWebRTCAPI) error { v := api if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, - PayloadType: 111, + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, + SDPFmtpLine: "minptime=10;useinbandfec=1", RTCPFeedback: nil, + }, + PayloadType: 111, }, webrtc.RTPCodecTypeAudio); err != nil { return err } - videoRTCPFeedback := []webrtc.RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}} + videoRTCPFeedback := []webrtc.RTCPFeedback{ + {Type: "goog-remb", Parameter: ""}, + {Type: "ccm", Parameter: "fir"}, + } if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback}, - PayloadType: 108, + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, ClockRate: 90000, Channels: 0, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", + RTCPFeedback: videoRTCPFeedback, + }, + PayloadType: 108, }, webrtc.RTPCodecTypeVideo); err != nil { return err } @@ -680,17 +709,28 @@ func registerHEVCCodecs(api *testWebRTCAPI) error { // Register Opus audio codec if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil}, - PayloadType: 111, + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, + SDPFmtpLine: "minptime=10;useinbandfec=1", RTCPFeedback: nil, + }, + PayloadType: 111, }, webrtc.RTPCodecTypeAudio); err != nil { return err } // Register HEVC/H.265 video codec - videoRTCPFeedback := []webrtc.RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}} + videoRTCPFeedback := []webrtc.RTCPFeedback{ + {Type: "goog-remb", Parameter: ""}, + {Type: "ccm", Parameter: "fir"}, + {Type: "nack", Parameter: ""}, + {Type: "nack", Parameter: "pli"}, + } if err := v.mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{webrtc.MimeTypeH265, 90000, 0, "profile-id=1", videoRTCPFeedback}, - PayloadType: 49, // Use payload type 49 for HEVC as mentioned in PR description + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH265, ClockRate: 90000, Channels: 0, + SDPFmtpLine: "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST", RTCPFeedback: videoRTCPFeedback, + }, + PayloadType: 49, // Use payload type 49 for HEVC as mentioned in PR description }, webrtc.RTPCodecTypeVideo); err != nil { return err } @@ -812,6 +852,8 @@ type testPlayer struct { api *testWebRTCAPI // Optional suffix for stream url. streamSuffix string + // Optional codec for stream, e.g., "hevc", "h264". + streamCodec string // Optional app/stream to play, use srsStream by default. defaultStream string } @@ -864,6 +906,13 @@ func (v *testPlayer) Run(ctx context.Context, cancel context.CancelFunc) error { if v.streamSuffix != "" { r = fmt.Sprintf("%v-%v", r, v.streamSuffix) } + if v.streamCodec != "" { + if strings.Contains(r, "?") { + r = fmt.Sprintf("%v&codec=%v", r, v.streamCodec) + } else { + r = fmt.Sprintf("%v?codec=%v", r, v.streamCodec) + } + } pli := time.Duration(*srsPlayPLI) * time.Millisecond logger.Tf(ctx, "Run play url=%v", r) @@ -1634,7 +1683,7 @@ func (v *RTMPPublisher) Publish(ctx context.Context, rtmpUrl string) error { return v.client.Publish(ctx, rtmpUrl) } -func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error { +func (v *RTMPPublisher) Ingest(ctx context.Context, input string) error { // If ctx is cancelled, close the RTMP transport. var wg sync.WaitGroup defer wg.Wait() @@ -1649,8 +1698,16 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error { }() // Consume all packets. - logger.Tf(ctx, "Start to ingest %v", flvInput) - err := v.ingest(ctx, flvInput) + logger.Tf(ctx, "Start to ingest %v", input) + + // Check file extension to determine format + var err error + if strings.HasSuffix(strings.ToLower(input), ".h265") { + err = v.ingestH265(ctx, input) + } else { + // Default to FLV format for H.264 + err = v.ingestFLV(ctx, input) + } if err == io.EOF { return nil } @@ -1660,7 +1717,7 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error { return err } -func (v *RTMPPublisher) ingest(ctx context.Context, flvInput string) error { +func (v *RTMPPublisher) ingestFLV(ctx context.Context, flvInput string) error { p := v.client fs, err := os.Open(flvInput) @@ -1718,6 +1775,247 @@ func (v *RTMPPublisher) ingest(ctx context.Context, flvInput string) error { return nil } +func (v *RTMPPublisher) ingestH265(ctx context.Context, h265Input string) error { + p := v.client + + fs, err := os.Open(h265Input) + if err != nil { + return err + } + defer fs.Close() + logger.Tf(ctx, "Open H.265 input %v", h265Input) + + h265Reader, err := h265reader.NewReader(fs) + if err != nil { + return err + } + + // Send sequence header first + var vps, sps, pps []byte + var timestamp uint64 = 0 + + // Read NALUs to find VPS, SPS, PPS + for { + nal, err := h265Reader.NextNAL() + if err != nil { + if err == io.EOF { + break + } + return err + } + + if nal == nil { + break + } + + // Extract parameter sets using pion constants + switch nal.NalUnitType { + case h265reader.NalUnitTypeVps: // VPS (32) + vps = nal.Data + case h265reader.NalUnitTypeSps: // SPS (33) + sps = nal.Data + case h265reader.NalUnitTypePps: // PPS (34) + pps = nal.Data + } + + // Once we have all parameter sets, send sequence header + if len(vps) > 0 && len(sps) > 0 && len(pps) > 0 { + if err := v.sendH265SequenceHeader(p, vps, sps, pps, timestamp); err != nil { + return err + } + break + } + } + + // Reset reader for actual frame data + fs.Seek(0, 0) + h265Reader, err = h265reader.NewReader(fs) + if err != nil { + return err + } + + // Send video frames + frameCount := 0 + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + nal, err := h265Reader.NextNAL() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + if nal == nil { + return nil + } + + // Skip parameter sets as they were already sent + if nal.NalUnitType == h265reader.NalUnitTypeVps || nal.NalUnitType == h265reader.NalUnitTypeSps || nal.NalUnitType == h265reader.NalUnitTypePps { + continue + } + + // Send video frame - check for IDR and CRA frames (key frames) + isKeyFrame := (nal.NalUnitType >= h265reader.NalUnitTypeBlaWLp && nal.NalUnitType <= h265reader.NalUnitTypeCraNut) + if err := v.sendH265Frame(p, nal.Data, timestamp, isKeyFrame); err != nil { + return err + } + + frameCount++ + timestamp += 40 // 25fps = 40ms per frame + + if v.onSendPacket != nil { + m := rtmp.NewStreamMessage(p.streamID) + m.MessageType = rtmp.MessageTypeVideo + m.Timestamp = timestamp + m.Payload = nal.Data + if err = v.onSendPacket(m); err != nil { + return err + } + } + } +} + +func (v *RTMPPublisher) sendH265SequenceHeader(p *RTMPClient, vps, sps, pps []byte, timestamp uint64) error { + // Create HEVC sequence header using enhanced-RTMP format + // Format: [IsExHeader | FrameType | PacketType] [fourcc 'hvc1'] [HEVCDecoderConfigurationRecord] + // @see: https://veovera.org/docs/enhanced/enhanced-rtmp-v1.pdf, page 9 + + const flvIsExHeader = 0x80 + const videoAvcFrameTypeKeyFrame = 1 + const videoHEVCFrameTraitPacketTypeSequenceStart = 0 + + // IsExHeader | FrameType | PacketType + frameTypeAndPacket := byte(flvIsExHeader | (videoAvcFrameTypeKeyFrame << 4) | videoHEVCFrameTraitPacketTypeSequenceStart) + + // Enhanced-RTMP fourcc 'hvc1' for HEVC (0x68766331) + fourcc := []byte{'h', 'v', 'c', '1'} + + // Create proper HEVCDecoderConfigurationRecord + hvcc := v.createHVCC(vps, sps, pps) + + // Build enhanced-RTMP packet + var payload []byte + payload = append(payload, frameTypeAndPacket) + payload = append(payload, fourcc...) + payload = append(payload, hvcc...) + + m := rtmp.NewStreamMessage(p.streamID) + m.MessageType = rtmp.MessageTypeVideo + m.Timestamp = timestamp + m.Payload = payload + + return p.proto.WriteMessage(m) +} + +func (v *RTMPPublisher) createHVCC(vps, sps, pps []byte) []byte { + // Create HEVCDecoderConfigurationRecord based on SRS format + // @see: trunk/src/protocol/srs_protocol_raw_avc.cpp mux_sequence_header + + var hvcc []byte + + // configuration_version (1 byte) - must be 1 + hvcc = append(hvcc, 0x01) + + // general_profile_space (2 bits) + general_tier_flag (1 bit) + general_profile_idc (5 bits) + hvcc = append(hvcc, 0x01) // simplified: profile_space=0, tier_flag=0, profile_idc=1 + + // general_profile_compatibility_flags (4 bytes) + hvcc = append(hvcc, 0x60, 0x00, 0x00, 0x00) + + // general_constraint_indicator_flags (6 bytes) + hvcc = append(hvcc, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00) + + // general_level_idc (1 byte) + hvcc = append(hvcc, 0x5d) // Level 3.1 + + // min_spatial_segmentation_idc (12 bits) + reserved (4 bits) + hvcc = append(hvcc, 0xf0, 0x00) + + // parallelismType (2 bits) + reserved (6 bits) + hvcc = append(hvcc, 0xfc) + + // chromaFormat (2 bits) + reserved (6 bits) + hvcc = append(hvcc, 0xfd) + + // bitDepthLumaMinus8 (3 bits) + reserved (5 bits) + hvcc = append(hvcc, 0xf8) + + // bitDepthChromaMinus8 (3 bits) + reserved (5 bits) + hvcc = append(hvcc, 0xf8) + + // avgFrameRate (2 bytes) + hvcc = append(hvcc, 0x00, 0x00) + + // constantFrameRate (2 bits) + numTemporalLayers (3 bits) + temporalIdNested (1 bit) + lengthSizeMinusOne (2 bits) + hvcc = append(hvcc, 0x0f) // lengthSizeMinusOne = 3 (4-byte length) + + // numOfArrays (1 byte) - we have 3 arrays: VPS, SPS, PPS + hvcc = append(hvcc, 0x03) + + // VPS array + hvcc = append(hvcc, 0x20) // array_completeness=0, reserved=0, NAL_unit_type=32 (VPS) + hvcc = append(hvcc, 0x00, 0x01) // numNalus = 1 + hvcc = append(hvcc, byte(len(vps)>>8), byte(len(vps))) // nalUnitLength + hvcc = append(hvcc, vps...) + + // SPS array + hvcc = append(hvcc, 0x21) // array_completeness=0, reserved=0, NAL_unit_type=33 (SPS) + hvcc = append(hvcc, 0x00, 0x01) // numNalus = 1 + hvcc = append(hvcc, byte(len(sps)>>8), byte(len(sps))) // nalUnitLength + hvcc = append(hvcc, sps...) + + // PPS array + hvcc = append(hvcc, 0x22) // array_completeness=0, reserved=0, NAL_unit_type=34 (PPS) + hvcc = append(hvcc, 0x00, 0x01) // numNalus = 1 + hvcc = append(hvcc, byte(len(pps)>>8), byte(len(pps))) // nalUnitLength + hvcc = append(hvcc, pps...) + + return hvcc +} + +func (v *RTMPPublisher) sendH265Frame(p *RTMPClient, nalData []byte, timestamp uint64, isKeyFrame bool) error { + // Create HEVC frame packet using enhanced-RTMP format + // Format: [IsExHeader | FrameType | PacketType] [fourcc 'hvc1'] [NALU data] + // @see: https://veovera.org/docs/enhanced/enhanced-rtmp-v1.pdf, page 9 + + const flvIsExHeader = 0x80 + const videoAvcFrameTypeKeyFrame = 1 + const videoAvcFrameTypeInterFrame = 2 + const videoHEVCFrameTraitPacketTypeCodedFramesX = 3 + + var frameType byte = videoAvcFrameTypeInterFrame + if isKeyFrame { + frameType = videoAvcFrameTypeKeyFrame + } + + // IsExHeader | FrameType | PacketType + frameTypeAndPacket := byte(flvIsExHeader | (frameType << 4) | videoHEVCFrameTraitPacketTypeCodedFramesX) + + // Enhanced-RTMP fourcc 'hvc1' for HEVC (0x68766331) + fourcc := []byte{'h', 'v', 'c', '1'} + + var payload []byte + payload = append(payload, frameTypeAndPacket) + payload = append(payload, fourcc...) + + // Add NALU length and data (IBMF format) + payload = append(payload, byte(len(nalData)>>24), byte(len(nalData)>>16), byte(len(nalData)>>8), byte(len(nalData))) + payload = append(payload, nalData...) + + m := rtmp.NewStreamMessage(p.streamID) + m.MessageType = rtmp.MessageTypeVideo + m.Timestamp = timestamp + m.Payload = payload + + return p.proto.WriteMessage(m) +} + type RTMPPlayer struct { // Transport. client *RTMPClient @@ -1922,7 +2220,7 @@ func (v *FLVPlayer) consume(ctx context.Context) (err error) { } } -func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool { +func isAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool { if a == nil || b == nil { return false } @@ -1936,13 +2234,13 @@ func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool { } for i := 0; i < len(a.SequenceParameterSetNALUnits); i++ { - if !IsNALUEquals(a.SequenceParameterSetNALUnits[i], b.SequenceParameterSetNALUnits[i]) { + if !isNALUEquals(a.SequenceParameterSetNALUnits[i], b.SequenceParameterSetNALUnits[i]) { return false } } for i := 0; i < len(a.PictureParameterSetNALUnits); i++ { - if !IsNALUEquals(a.PictureParameterSetNALUnits[i], b.PictureParameterSetNALUnits[i]) { + if !isNALUEquals(a.PictureParameterSetNALUnits[i], b.PictureParameterSetNALUnits[i]) { return false } } @@ -1950,7 +2248,7 @@ func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool { return true } -func IsNALUEquals(a, b *avc.NALU) bool { +func isNALUEquals(a, b *avc.NALU) bool { if a == nil || b == nil { return false } @@ -1962,7 +2260,7 @@ func IsNALUEquals(a, b *avc.NALU) bool { return bytes.Equal(a.Data, b.Data) } -func DemuxRtpSpsPps(payload []byte) ([]byte, []*avc.NALU, error) { +func demuxRtpSpsPps(payload []byte) ([]byte, []*avc.NALU, error) { // Parse RTP packet. pkt := rtp.Packet{} if err := pkt.Unmarshal(payload); 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 a3235bec2..120faf29b 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 @@ -19,23 +19,42 @@ linters-settings: recommendations: - 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 + - 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`. @@ -46,18 +65,17 @@ linters: - 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 - - 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 - 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 @@ -65,9 +83,15 @@ linters: - 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 @@ -75,28 +99,22 @@ linters: - 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 + - 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 - - containedctx # containedctx is a linter that detects struct contained context.Context field - - cyclop # checks function and package cyclomatic complexity - 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. + - 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 - - lll # Reports long lines - - maintidx # maintidx measures the maintainability index of each function. - - makezero # Finds slice declarations with non-zero initial length - - 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 + - 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 @@ -104,8 +122,7 @@ linters: - 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 + - 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! @@ -114,9 +131,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands 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 5db5d4414..46a9ca6a2 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 @@ -3,10 +3,10 @@ Pion Interceptor
-

RTCP and RTCP processors for building real time communications

+

RTP and RTCP processors for building real time communications

Pion Interceptor - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status Go Reference @@ -36,12 +36,12 @@ by anyone. With the following tenets in mind. * [Google Congestion Control](https://github.com/pion/interceptor/tree/master/pkg/gcc) * [Stats](https://github.com/pion/interceptor/tree/master/pkg/stats) A [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation * [Interval PLI](https://github.com/pion/interceptor/tree/master/pkg/intervalpli) Generate PLI on a interval. Useful when no decoder is available. +* [FlexFec](https://github.com/pion/interceptor/tree/master/pkg/flexfec) – [FlexFEC-03](https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03) encoder implementation ### Planned Interceptors * Bandwidth Estimation - [NADA](https://tools.ietf.org/html/rfc8698) * JitterBuffer, re-order packets and wait for arrival -* [FlexFec](https://tools.ietf.org/html/draft-ietf-payload-flexible-fec-scheme-20) * [RTCP Feedback for Congestion Control](https://datatracker.ietf.org/doc/html/rfc8888) the standardized alternative to TWCC. ### Interceptor Public API @@ -70,9 +70,9 @@ You should also look in [pion/webrtc](https://github.com/pion/webrtc) for real w 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) 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 8b6d0f5cf..e4257b8da 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 @@ -19,7 +19,7 @@ const ( var errInvalidType = errors.New("found value of invalid type in attributes map") -// Attributes are a generic key/value store used by interceptors +// Attributes are a generic key/value store used by interceptors. type Attributes map[interface{}]interface{} // Get returns the attribute associated with key. @@ -39,6 +39,7 @@ func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) { if header, ok := val.(*rtp.Header); ok { return header, nil } + return nil, errInvalidType } header := &rtp.Header{} @@ -46,6 +47,7 @@ func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) { return nil, err } a[rtpHeaderKey] = header + return header, nil } @@ -57,6 +59,7 @@ func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) { if packets, ok := val.([]rtcp.Packet); ok { return packets, nil } + return nil, errInvalidType } pkts, err := rtcp.Unmarshal(raw) @@ -64,5 +67,6 @@ func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) { return nil, err } a[rtcpPacketsKey] = pkts + return pkts, nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/chain.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/chain.go index 267f36651..62c0aeb12 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/chain.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/chain.go @@ -50,7 +50,8 @@ func (i *Chain) UnbindLocalStream(ctx *StreamInfo) { } } -// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method +// 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 (i *Chain) BindRemoteStream(ctx *StreamInfo, reader RTPReader) RTPReader { for _, interceptor := range i.interceptors { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/errors.go index 3dafee3ef..02c79539c 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/errors.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/errors.go @@ -18,6 +18,7 @@ func flattenErrs(errs []error) error { if len(errs2) == 0 { return nil } + return multiError(errs2) } @@ -50,5 +51,6 @@ func (me multiError) Is(err error) bool { } } } + return false } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/interceptor.go index c6ba53242..10d3e32fc 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/interceptor.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/interceptor.go @@ -12,7 +12,7 @@ import ( "github.com/pion/rtp" ) -// Factory provides an interface for constructing interceptors +// Factory provides an interface for constructing interceptors. type Factory interface { NewInterceptor(id string) (Interceptor, error) } @@ -35,7 +35,8 @@ type Interceptor interface { // UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track. UnbindLocalStream(info *StreamInfo) - // BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method + // 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. BindRemoteStream(info *StreamInfo, reader RTPReader) RTPReader @@ -69,34 +70,34 @@ type RTCPReader interface { Read([]byte, Attributes) (int, Attributes, error) } -// RTPWriterFunc is an adapter for RTPWrite interface +// RTPWriterFunc is an adapter for RTPWrite interface. type RTPWriterFunc func(header *rtp.Header, payload []byte, attributes Attributes) (int, error) -// RTPReaderFunc is an adapter for RTPReader interface +// RTPReaderFunc is an adapter for RTPReader interface. type RTPReaderFunc func([]byte, Attributes) (int, Attributes, error) -// RTCPWriterFunc is an adapter for RTCPWriter interface +// RTCPWriterFunc is an adapter for RTCPWriter interface. type RTCPWriterFunc func(pkts []rtcp.Packet, attributes Attributes) (int, error) -// RTCPReaderFunc is an adapter for RTCPReader interface +// RTCPReaderFunc is an adapter for RTCPReader interface. type RTCPReaderFunc func([]byte, Attributes) (int, Attributes, error) -// Write a rtp packet +// Write a rtp packet. func (f RTPWriterFunc) Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error) { return f(header, payload, attributes) } -// Read a rtp packet +// Read a rtp packet. func (f RTPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) { return f(b, a) } -// Write a batch of rtcp packets +// Write a batch of rtcp packets. func (f RTCPWriterFunc) Write(pkts []rtcp.Packet, attributes Attributes) (int, error) { return f(pkts, attributes) } -// Read a batch of rtcp packets +// Read a batch of rtcp packets. func (f RTCPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) { return f(b, a) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/ntp/ntp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/ntp/ntp.go index 69f98d6c9..266f13d88 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/ntp/ntp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/ntp/ntp.go @@ -9,7 +9,7 @@ import ( "time" ) -// ToNTP converts a time.Time oboject to an uint64 NTP timestamp +// ToNTP converts a time.Time oboject to an uint64 NTP timestamp. func ToNTP(t time.Time) uint64 { // seconds since 1st January 1900 s := (float64(t.UnixNano()) / 1000000000) + 2208988800 @@ -17,14 +17,31 @@ func ToNTP(t time.Time) uint64 { // higher 32 bits are the integer part, lower 32 bits are the fractional part integerPart := uint32(s) fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF) - return uint64(integerPart)<<32 | uint64(fractionalPart) + + return uint64(integerPart)<<32 | uint64(fractionalPart) //nolint:gosec // G115 } -// ToTime converts a uint64 NTP timestamps to a time.Time object +// ToNTP32 converts a time.Time object to a uint32 NTP timestamp. +func ToNTP32(t time.Time) uint32 { + return uint32(ToNTP(t) >> 16) //nolint:gosec // G115 +} + +// ToTime converts a uint64 NTP timestamps to a time.Time object. func ToTime(t uint64) time.Time { seconds := (t & 0xFFFFFFFF00000000) >> 32 fractional := float64(t&0x00000000FFFFFFFF) / float64(0xFFFFFFFF) + //nolint:gosec // G115 d := time.Duration(seconds)*time.Second + time.Duration(fractional*1e9)*time.Nanosecond return time.Unix(0, 0).Add(-2208988800 * time.Second).Add(d) } + +// ToTime32 converts a uint32 NTP timestamp to a time.Time object, using the +// highest 16 bit of the reference to recover the lost bits. The low 16 bits are +// not recovered. +func ToTime32(t uint32, reference time.Time) time.Time { + referenceNTP := ToNTP(reference) & 0xFFFF000000000000 + tu64 := ((uint64(t) << 16) & 0x0000FFFFFFFF0000) | referenceNTP + + return ToTime(tu64) +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go new file mode 100644 index 000000000..4c3112ba2 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/errors.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package rtpbuffer + +import "errors" + +// ErrInvalidSize is returned by newReceiveLog/newRTPBuffer, when an incorrect buffer size is supplied. +var ErrInvalidSize = errors.New("invalid buffer size") + +var ( + errPacketReleased = errors.New("could not retain packet, already released") + errFailedToCastHeaderPool = errors.New("could not access header pool, failed cast") + errFailedToCastPayloadPool = errors.New("could not access payload pool, failed cast") + errPaddingOverflow = errors.New("padding size exceeds payload size") +) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go new file mode 100644 index 000000000..a9a8d36fb --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/packet_factory.go @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package rtpbuffer + +import ( + "encoding/binary" + "io" + "sync" + + "github.com/pion/rtp" +) + +const rtxSsrcByteLength = 2 + +// PacketFactory allows custom logic around the handle of RTP Packets before they added to the RTPBuffer. +// The NoOpPacketFactory doesn't copy packets, while the RetainablePacket will take a copy before adding. +type PacketFactory interface { + NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*RetainablePacket, error) +} + +// PacketFactoryCopy is PacketFactory that takes a copy of packets when added to the RTPBuffer. +type PacketFactoryCopy struct { + headerPool *sync.Pool + payloadPool *sync.Pool + rtxSequencer rtp.Sequencer +} + +// NewPacketFactoryCopy constructs a PacketFactory that takes a copy of packets when added to the RTPBuffer. +func NewPacketFactoryCopy() *PacketFactoryCopy { + return &PacketFactoryCopy{ + headerPool: &sync.Pool{ + New: func() interface{} { + return &rtp.Header{} + }, + }, + payloadPool: &sync.Pool{ + New: func() interface{} { + buf := make([]byte, maxPayloadLen) + + return &buf + }, + }, + rtxSequencer: rtp.NewRandomSequencer(), + } +} + +// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer. +// +//nolint:cyclop +func (m *PacketFactoryCopy) NewPacket( + header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8, +) (*RetainablePacket, error) { + if len(payload) > maxPayloadLen { + return nil, io.ErrShortBuffer + } + + retainablePacket := &RetainablePacket{ + onRelease: m.releasePacket, + sequenceNumber: header.SequenceNumber, + // new packets have retain count of 1 + count: 1, + } + + var ok bool + retainablePacket.header, ok = m.headerPool.Get().(*rtp.Header) + if !ok { + return nil, errFailedToCastHeaderPool + } + + *retainablePacket.header = header.Clone() + + if payload != nil { + retainablePacket.buffer, ok = m.payloadPool.Get().(*[]byte) + if !ok { + return nil, errFailedToCastPayloadPool + } + if rtxSsrc != 0 && rtxPayloadType != 0 { + size := copy((*retainablePacket.buffer)[rtxSsrcByteLength:], payload) + retainablePacket.payload = (*retainablePacket.buffer)[:size+rtxSsrcByteLength] + } else { + size := copy(*retainablePacket.buffer, payload) + retainablePacket.payload = (*retainablePacket.buffer)[:size] + } + } + + if rtxSsrc != 0 && rtxPayloadType != 0 { //nolint:nestif + if payload == nil { + retainablePacket.buffer, ok = m.payloadPool.Get().(*[]byte) + if !ok { + return nil, errFailedToCastPayloadPool + } + retainablePacket.payload = (*retainablePacket.buffer)[:rtxSsrcByteLength] + } + // Write the original sequence number at the beginning of the payload. + binary.BigEndian.PutUint16(retainablePacket.payload, retainablePacket.header.SequenceNumber) + + // Rewrite the SSRC. + retainablePacket.header.SSRC = rtxSsrc + // Rewrite the payload type. + retainablePacket.header.PayloadType = rtxPayloadType + // Rewrite the sequence number. + retainablePacket.header.SequenceNumber = m.rtxSequencer.NextSequenceNumber() + // Remove padding if present. + if retainablePacket.header.Padding { + // Older versions of pion/rtp didn't have the Header.PaddingSize field and as a workaround + // users had to add padding to the payload. We need to handle this case here. + if retainablePacket.header.PaddingSize == 0 && len(retainablePacket.payload) > 0 { + paddingLength := int(retainablePacket.payload[len(retainablePacket.payload)-1]) + if paddingLength > len(retainablePacket.payload) { + return nil, errPaddingOverflow + } + retainablePacket.payload = (*retainablePacket.buffer)[:len(retainablePacket.payload)-paddingLength] + } + + retainablePacket.header.Padding = false + retainablePacket.header.PaddingSize = 0 + } + } + + return retainablePacket, nil +} + +func (m *PacketFactoryCopy) releasePacket(header *rtp.Header, payload *[]byte) { + m.headerPool.Put(header) + if payload != nil { + m.payloadPool.Put(payload) + } +} + +// PacketFactoryNoOp is a PacketFactory implementation that doesn't copy packets. +type PacketFactoryNoOp struct{} + +// NewPacket constructs a new RetainablePacket that can be added to the RTPBuffer. +func (f *PacketFactoryNoOp) NewPacket( + header *rtp.Header, payload []byte, _ uint32, _ uint8, +) (*RetainablePacket, error) { + return &RetainablePacket{ + onRelease: f.releasePacket, + count: 1, + header: header, + payload: payload, + sequenceNumber: header.SequenceNumber, + }, nil +} + +func (f *PacketFactoryNoOp) releasePacket(_ *rtp.Header, _ *[]byte) { + // no-op +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go new file mode 100644 index 000000000..82ade097c --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/retainable_packet.go @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package rtpbuffer + +import ( + "sync" + + "github.com/pion/rtp" +) + +// RetainablePacket is a referenced counted RTP packet. +type RetainablePacket struct { + onRelease func(*rtp.Header, *[]byte) + + countMu sync.Mutex + count int + + header *rtp.Header + buffer *[]byte + payload []byte + + sequenceNumber uint16 +} + +// Header returns the RTP Header of the RetainablePacket. +func (p *RetainablePacket) Header() *rtp.Header { + return p.header +} + +// Payload returns the RTP Payload of the RetainablePacket. +func (p *RetainablePacket) Payload() []byte { + return p.payload +} + +// Retain increases the reference count of the RetainablePacket. +func (p *RetainablePacket) Retain() error { + p.countMu.Lock() + defer p.countMu.Unlock() + if p.count == 0 { + // already released + return errPacketReleased + } + p.count++ + + return nil +} + +// Release decreases the reference count of the RetainablePacket and frees if needed. +func (p *RetainablePacket) Release() { + p.countMu.Lock() + defer p.countMu.Unlock() + p.count-- + + if p.count == 0 { + // release back to pool + p.onRelease(p.header, p.buffer) + p.header = nil + p.buffer = nil + p.payload = nil + } +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go new file mode 100644 index 000000000..94c7adfe1 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/internal/rtpbuffer/rtpbuffer.go @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package rtpbuffer provides a buffer for storing RTP packets +package rtpbuffer + +import ( + "fmt" +) + +const ( + // Uint16SizeHalf is half of a math.Uint16. + Uint16SizeHalf = 1 << 15 + + maxPayloadLen = 1460 +) + +// RTPBuffer stores RTP packets and allows custom logic +// around the lifetime of them via the PacketFactory. +type RTPBuffer struct { + packets []*RetainablePacket + size uint16 + highestAdded uint16 + started bool +} + +// NewRTPBuffer constructs a new RTPBuffer. +func NewRTPBuffer(size uint16) (*RTPBuffer, error) { + allowedSizes := make([]uint16, 0) + correctSize := false + for i := 0; i < 16; i++ { + if size == 1<= Uint16SizeHalf { + return nil + } + + if diff >= r.size { + return nil + } + + pkt := r.packets[seq%r.size] + if pkt != nil { + if pkt.sequenceNumber != seq { + return nil + } + // already released + if err := pkt.Retain(); err != nil { + return nil + } + } + + return pkt +} 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 index 311ff09df..48500b3cc 100644 --- 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 @@ -9,7 +9,7 @@ const ( breakpoint = 32768 // half of max uint16 ) -// Unwrapper stores an unwrapped sequence number +// Unwrapper stores an unwrapped sequence number. type Unwrapper struct { init bool lastUnwrapped int64 @@ -19,18 +19,20 @@ func isNewer(value, previous uint16) bool { if value-previous == breakpoint { return value > previous } + return value != previous && (value-previous) < breakpoint } -// Unwrap unwraps the next sequencenumber +// 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) + lastWrapped := uint16(u.lastUnwrapped) //nolint:gosec // G115 delta := int64(i - lastWrapped) if isNewer(i, lastWrapped) { if delta < 0 { @@ -41,5 +43,6 @@ func (u *Unwrapper) Unwrap(i uint16) int64 { } u.lastUnwrapped += delta + return u.lastUnwrapped } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/noop.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/noop.go index b0fc2a69a..964a330e8 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/noop.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/noop.go @@ -28,7 +28,8 @@ func (i *NoOp) BindLocalStream(_ *StreamInfo, writer RTPWriter) RTPWriter { // UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track. func (i *NoOp) UnbindLocalStream(_ *StreamInfo) {} -// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method +// 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 (i *NoOp) BindRemoteStream(_ *StreamInfo, reader RTPReader) RTPReader { return reader diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/encoder_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/encoder_interceptor.go new file mode 100644 index 000000000..ee627e40a --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/encoder_interceptor.go @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package flexfec + +import ( + "errors" + "sync" + + "github.com/pion/interceptor" + "github.com/pion/rtp" +) + +// streamState holds the state for a single stream. +type streamState struct { + mu sync.Mutex + flexFecEncoder FlexEncoder + packetBuffer []rtp.Packet +} + +// FecInterceptor implements FlexFec. +type FecInterceptor struct { + interceptor.NoOp + mu sync.Mutex + streams map[uint32]*streamState + numMediaPackets uint32 + numFecPackets uint32 + encoderFactory EncoderFactory +} + +// FecInterceptorFactory creates new FecInterceptors. +type FecInterceptorFactory struct { + opts []FecOption +} + +// NewFecInterceptor returns a new Fec interceptor factory. +func NewFecInterceptor(opts ...FecOption) (*FecInterceptorFactory, error) { + return &FecInterceptorFactory{opts: opts}, nil +} + +// NewInterceptor constructs a new FecInterceptor. +func (r *FecInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { + interceptor := &FecInterceptor{ + streams: make(map[uint32]*streamState), + numMediaPackets: 5, + numFecPackets: 2, + encoderFactory: FlexEncoder03Factory{}, + } + + for _, opt := range r.opts { + if err := opt(interceptor); err != nil { + return nil, err + } + } + + return interceptor, nil +} + +// UnbindLocalStream removes the stream state for a specific SSRC. +func (r *FecInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) { + r.mu.Lock() + defer r.mu.Unlock() + + delete(r.streams, info.SSRC) +} + +// 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 (r *FecInterceptor) BindLocalStream( + info *interceptor.StreamInfo, writer interceptor.RTPWriter, +) interceptor.RTPWriter { + if info.PayloadTypeForwardErrorCorrection == 0 || info.SSRCForwardErrorCorrection == 0 { + return writer + } + + mediaSSRC := info.SSRC + + r.mu.Lock() + stream := &streamState{ + // Chromium supports version flexfec-03 of existing draft, this is the one we will configure by default + // although we should support configuring the latest (flexfec-20) as well. + flexFecEncoder: r.encoderFactory.NewEncoder(info.PayloadTypeForwardErrorCorrection, info.SSRCForwardErrorCorrection), + packetBuffer: make([]rtp.Packet, 0), + } + r.streams[mediaSSRC] = stream + r.mu.Unlock() + + return interceptor.RTPWriterFunc( + func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { + // Ignore non-media packets + if header.SSRC != mediaSSRC { + return writer.Write(header, payload, attributes) + } + + var fecPackets []rtp.Packet + stream.mu.Lock() + stream.packetBuffer = append(stream.packetBuffer, rtp.Packet{ + Header: *header, + Payload: payload, + }) + + // Check if we have enough packets to generate FEC + if len(stream.packetBuffer) == int(r.numMediaPackets) { + fecPackets = stream.flexFecEncoder.EncodeFec(stream.packetBuffer, r.numFecPackets) + // Reset the packet buffer now that we've sent the corresponding FEC packets. + stream.packetBuffer = nil + } + stream.mu.Unlock() + + var errs []error + result, err := writer.Write(header, payload, attributes) + if err != nil { + errs = append(errs, err) + } + + for _, packet := range fecPackets { + header := packet.Header + + _, err = writer.Write(&header, packet.Payload, attributes) + if err != nil { + errs = append(errs, err) + } + } + + return result, errors.Join(errs...) + }, + ) +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_coverage.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_coverage.go new file mode 100644 index 000000000..1ffeefb5d --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_coverage.go @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package flexfec + +import ( + "github.com/pion/interceptor/pkg/flexfec/util" + "github.com/pion/rtp" +) + +// Maximum number of media packets that can be protected by a single FEC packet. +// We are not supporting the possibility of having an FEC packet protect multiple +// SSRC source packets for now. +// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1 +const ( + MaxMediaPackets uint32 = 110 + MaxFecPackets uint32 = MaxMediaPackets +) + +// ProtectionCoverage defines the map of RTP packets that individual Fec packets protect. +type ProtectionCoverage struct { + // Array of masks, each mask capable of covering up to maxMediaPkts = 110. + // A mask is represented as a grouping of bytes where each individual bit + // represents the coverage for the media packet at the corresponding index. + packetMasks [MaxFecPackets]util.BitArray + numFecPackets uint32 + numMediaPackets uint32 + mediaPackets []rtp.Packet +} + +// NewCoverage returns a new ProtectionCoverage object. numFecPackets represents the number of +// Fec packets that we will be generating to cover the list of mediaPackets. This allows us to know +// how big the underlying map should be. +func NewCoverage(mediaPackets []rtp.Packet, numFecPackets uint32) *ProtectionCoverage { + numMediaPackets := uint32(len(mediaPackets)) //nolint:gosec // G115 + + // Basic sanity checks + if numMediaPackets <= 0 || numMediaPackets > MaxMediaPackets { + return nil + } + + // We allocate the biggest array of bitmasks that respects the max constraints. + var packetMasks [MaxFecPackets]util.BitArray + for i := 0; i < int(MaxFecPackets); i++ { + packetMasks[i] = util.BitArray{} + } + + coverage := &ProtectionCoverage{ + packetMasks: packetMasks, + numFecPackets: 0, + numMediaPackets: 0, + mediaPackets: nil, + } + + coverage.UpdateCoverage(mediaPackets, numFecPackets) + + return coverage +} + +// UpdateCoverage updates the ProtectionCoverage object with new bitmasks accounting for the numFecPackets +// we want to use to protect the batch media packets. +func (p *ProtectionCoverage) UpdateCoverage(mediaPackets []rtp.Packet, numFecPackets uint32) { + numMediaPackets := uint32(len(mediaPackets)) //nolint:gosec // G115 + + // Basic sanity checks + if numMediaPackets <= 0 || numMediaPackets > MaxMediaPackets { + return + } + + p.mediaPackets = mediaPackets + + if numFecPackets == p.numFecPackets && numMediaPackets == p.numMediaPackets { + // We have the same number of FEC packets covering the same number of media packets, we can simply + // reuse the previous coverage map with the updated media packets. + return + } + + p.numFecPackets = numFecPackets + p.numMediaPackets = numMediaPackets + + // The number of FEC packets and/or the number of packets has changed, we need to update the coverage map + // to reflect these new values. + p.resetCoverage() + + // Generate FEC bit mask where numFecPackets FEC packets are covering numMediaPackets Media packets. + // In the packetMasks array, each FEC packet is represented by a single BitArray, each bit in a given BitArray + // corresponds to a specific Media packet. + // Ex: Row I, Col J is set to 1 -> FEC packet I will protect media packet J. + for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + // We use an interleaved method to determine coverage. Given N FEC packets, Media packet X will be + // covered by FEC packet X % N. + coveredMediaPacketIndex := fecPacketIndex + for coveredMediaPacketIndex < numMediaPackets { + p.packetMasks[fecPacketIndex].SetBit(coveredMediaPacketIndex) + coveredMediaPacketIndex += numFecPackets + } + } +} + +// ResetCoverage clears the underlying map so that we can reuse it for new batches of RTP packets. +func (p *ProtectionCoverage) resetCoverage() { + for i := uint32(0); i < MaxFecPackets; i++ { + p.packetMasks[i].Reset() + } +} + +// GetCoveredBy returns an iterator over RTP packets that are protected by the specified Fec packet index. +func (p *ProtectionCoverage) GetCoveredBy(fecPacketIndex uint32) *util.MediaPacketIterator { + coverage := make([]uint32, 0, p.numMediaPackets) + for mediaPacketIndex := uint32(0); mediaPacketIndex < p.numMediaPackets; mediaPacketIndex++ { + if p.packetMasks[fecPacketIndex].GetBit(mediaPacketIndex) == 1 { + coverage = append(coverage, mediaPacketIndex) + } + } + + return util.NewMediaPacketIterator(p.mediaPackets, coverage) +} + +// ExtractMask1 returns the first section of the bitmask as defined by the FEC header. +// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1 +func (p *ProtectionCoverage) ExtractMask1(fecPacketIndex uint32) uint16 { + return extractMask1(p.packetMasks[fecPacketIndex]) +} + +// ExtractMask2 returns the second section of the bitmask as defined by the FEC header. +// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1 +func (p *ProtectionCoverage) ExtractMask2(fecPacketIndex uint32) uint32 { + return extractMask2(p.packetMasks[fecPacketIndex]) +} + +// ExtractMask3 returns the third section of the bitmask as defined by the FEC header. +// https://datatracker.ietf.org/doc/html/rfc8627#section-4.2.2.1 +func (p *ProtectionCoverage) ExtractMask3(fecPacketIndex uint32) uint64 { + return extractMask3(p.packetMasks[fecPacketIndex]) +} + +// ExtractMask3_03 returns the third section of the bitmask as defined by the FEC header. +// https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03#section-4.2 +func (p *ProtectionCoverage) ExtractMask3_03(fecPacketIndex uint32) uint64 { + return extractMask3_03(p.packetMasks[fecPacketIndex]) +} + +func extractMask1(mask util.BitArray) uint16 { + // We get the first 16 bits (64 - 16 -> shift by 48) and we shift once more for K field + mask1 := mask.Lo >> 49 + + return uint16(mask1) //nolint:gosec // G115 +} + +func extractMask2(mask util.BitArray) uint32 { + // We remove the first 15 bits + mask2 := mask.Lo << 15 + // We get the first 31 bits (64 - 32 -> shift by 32) and we shift once more for K field + mask2 >>= 33 + + return uint32(mask2) //nolint:gosec +} + +func extractMask3(mask util.BitArray) uint64 { + // We remove the first 46 bits + maskLo := mask.Lo << 46 + maskHi := mask.Hi >> 18 + mask3 := maskLo | maskHi + + return mask3 +} + +func extractMask3_03(mask util.BitArray) uint64 { + // We remove the first 46 bits + maskLo := mask.Lo << 46 + maskHi := mask.Hi >> 18 + mask3 := maskLo | maskHi + // We shift once for the K bit. + mask3 >>= 1 + + return mask3 +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go new file mode 100644 index 000000000..56ccff0b9 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_decoder_03.go @@ -0,0 +1,445 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package flexfec implements FlexFEC-03 to recover missing RTP packets due to packet loss. +// https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03 +package flexfec + +import ( + "encoding/binary" + "errors" + "fmt" + "sort" + + "github.com/pion/logging" + "github.com/pion/rtp" +) + +// Static errors for the flexfec package. +var ( + errPacketTruncated = errors.New("packet truncated") + errRetransmissionBitSet = errors.New("packet with retransmission bit set not supported") + errInflexibleGeneratorMatrix = errors.New("packet with inflexible generator matrix not supported") + errMultipleSSRCProtection = errors.New("multiple ssrc protection not supported") + errLastOptionalMaskKBitSetToFalse = errors.New("k-bit of last optional mask is set to false") +) + +// fecDecoder is a WIP implementation decoder used for testing purposes. +type fecDecoder struct { + logger logging.LeveledLogger + ssrc uint32 + protectedStreamSSRC uint32 + maxMediaPackets int + maxFECPackets int + recoveredPackets []rtp.Packet + receivedFECPackets []fecPacketState +} + +func newFECDecoder(ssrc uint32, protectedStreamSSRC uint32) *fecDecoder { + return &fecDecoder{ + logger: logging.NewDefaultLoggerFactory().NewLogger("fec_decoder"), + ssrc: ssrc, + protectedStreamSSRC: protectedStreamSSRC, + maxMediaPackets: 100, + maxFECPackets: 100, + recoveredPackets: make([]rtp.Packet, 0), + receivedFECPackets: make([]fecPacketState, 0), + } +} + +func (d *fecDecoder) DecodeFec(receivedPacket rtp.Packet) []rtp.Packet { + if len(d.recoveredPackets) == d.maxMediaPackets { + backRecoveredPacket := d.recoveredPackets[len(d.recoveredPackets)-1] + if backRecoveredPacket.SSRC == receivedPacket.SSRC { + seqDiffVal := seqDiff(receivedPacket.SequenceNumber, backRecoveredPacket.SequenceNumber) + if seqDiffVal > uint16(d.maxMediaPackets) { //nolint:gosec + d.logger.Info("big gap in media sequence numbers - resetting buffers") + d.recoveredPackets = nil + d.receivedFECPackets = nil + } + } + } + + d.insertPacket(receivedPacket) + + return d.attemptRecovery() +} + +func (d *fecDecoder) insertPacket(receivedPkt rtp.Packet) { + // Discard old FEC packets such that the sequence numbers in + // `received_fec_packets_` span at most 1/2 of the sequence number space. + // This is important for keeping `received_fec_packets_` sorted, and may + // also reduce the possibility of incorrect decoding due to sequence number + // wrap-around. + if len(d.receivedFECPackets) > 0 && receivedPkt.SSRC == d.ssrc { + toRemove := 0 + for _, fecPkt := range d.receivedFECPackets { + if abs(int(receivedPkt.SequenceNumber)-int(fecPkt.packet.SequenceNumber)) > 0x3fff { + toRemove++ + } else { + // No need to keep iterating, since |received_fec_packets_| is sorted. + break + } + } + } + + switch receivedPkt.SSRC { + case d.ssrc: + d.insertFECPacket(receivedPkt) + case d.protectedStreamSSRC: + d.insertMediaPacket(receivedPkt) + } + + d.discardOldRecoveredPackets() +} + +func (d *fecDecoder) insertMediaPacket(receivedPkt rtp.Packet) { + for _, recoveredPacket := range d.recoveredPackets { + if recoveredPacket.SequenceNumber == receivedPkt.SequenceNumber { + return + } + } + + d.recoveredPackets = append(d.recoveredPackets, receivedPkt) + sort.Slice(d.recoveredPackets, func(i, j int) bool { + return isNewerSeq(d.recoveredPackets[i].SequenceNumber, d.recoveredPackets[j].SequenceNumber) + }) + d.updateCoveringFecPackets(receivedPkt) +} + +func (d *fecDecoder) updateCoveringFecPackets(receivedPkt rtp.Packet) { + for _, fecPkt := range d.receivedFECPackets { + for _, protectedPacket := range fecPkt.protectedPackets { + if protectedPacket.seq == receivedPkt.SequenceNumber { + protectedPacket.packet = &receivedPkt + } + } + } +} + +func (d *fecDecoder) insertFECPacket(fecPkt rtp.Packet) { //nolint:cyclop + for _, existingFECPacket := range d.receivedFECPackets { + if existingFECPacket.packet.SequenceNumber == fecPkt.SequenceNumber { + return + } + } + + fec, err := parseFlexFEC03Header(fecPkt.Payload) + if err != nil { + d.logger.Errorf("failed to parse flexfec03 header: %v", err) + + return + } + + if fec.protectedSSRC != d.protectedStreamSSRC { + d.logger.Errorf("fec is protecting unknown ssrc, expected %d, got %d", fec.protectedSSRC, d.protectedStreamSSRC) + + return + } + + protectedSeqs := decodeMask(uint64(fec.mask0), 15, fec.seqNumBase) + if fec.mask1 != 0 { + protectedSeqs = append(protectedSeqs, decodeMask(uint64(fec.mask1), 31, fec.seqNumBase+15)...) + } + if fec.mask2 != 0 { + protectedSeqs = append(protectedSeqs, decodeMask(fec.mask2, 63, fec.seqNumBase+46)...) + } + + if len(protectedSeqs) == 0 { + d.logger.Warn("empty fec packet mask") + + return + } + + protectedPackets := make([]*protectedPacket, 0, len(protectedSeqs)) + protectedSeqIt := 0 + recoveredPacketIt := 0 + + for protectedSeqIt < len(protectedSeqs) && recoveredPacketIt < len(d.recoveredPackets) { + switch { + case isNewerSeq(protectedSeqs[protectedSeqIt], d.recoveredPackets[recoveredPacketIt].SequenceNumber): + protectedPackets = append(protectedPackets, &protectedPacket{ + seq: protectedSeqs[protectedSeqIt], + packet: nil, + }) + protectedSeqIt++ + case isNewerSeq(d.recoveredPackets[recoveredPacketIt].SequenceNumber, protectedSeqs[protectedSeqIt]): + recoveredPacketIt++ + default: + protectedPackets = append(protectedPackets, &protectedPacket{ + seq: protectedSeqs[protectedSeqIt], + packet: &d.recoveredPackets[recoveredPacketIt], + }) + protectedSeqIt++ + recoveredPacketIt++ + } + } + + for protectedSeqIt < len(protectedSeqs) { + protectedPackets = append(protectedPackets, &protectedPacket{ + seq: protectedSeqs[protectedSeqIt], + packet: nil, + }) + protectedSeqIt++ + } + d.receivedFECPackets = append(d.receivedFECPackets, fecPacketState{ + packet: fecPkt, + flexFec: fec, + protectedPackets: protectedPackets, + }) + + sort.Slice(d.receivedFECPackets, func(i, j int) bool { + return isNewerSeq(d.receivedFECPackets[i].packet.SequenceNumber, d.receivedFECPackets[j].packet.SequenceNumber) + }) + + if len(d.receivedFECPackets) > d.maxFECPackets { + d.receivedFECPackets = d.receivedFECPackets[1:] + } +} + +func (d *fecDecoder) attemptRecovery() []rtp.Packet { + recoveredPackets := make([]rtp.Packet, 0) + for { + packetsRecovered := 0 + for _, fecPkt := range d.receivedFECPackets { + packetsMissing := 0 + for _, pkt := range fecPkt.protectedPackets { + if pkt.packet == nil { + packetsMissing++ + if packetsMissing > 1 { + break + } + } + } + + if packetsMissing != 1 { + continue + } + + recovered, err := d.recoverPacket(&fecPkt) //nolint:gosec + if err != nil { + d.logger.Errorf("failed to recover packet: %v", err) + } + + recoveredPackets = append(recoveredPackets, recovered) + d.recoveredPackets = append(d.recoveredPackets, recovered) + sort.Slice(d.recoveredPackets, func(i, j int) bool { + return isNewerSeq(d.recoveredPackets[i].SequenceNumber, d.recoveredPackets[j].SequenceNumber) + }) + + d.updateCoveringFecPackets(recovered) + d.discardOldRecoveredPackets() + packetsRecovered++ + } + + if packetsRecovered == 0 { + break + } + } + + return recoveredPackets +} + +func (d *fecDecoder) recoverPacket(fec *fecPacketState) (rtp.Packet, error) { + // https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03#section-6.3.2 + + // 2. For the repair packet in T, extract the FEC bit string as the + // first 80 bits of the FEC header. + headerRecovery := make([]byte, 12) + copy(headerRecovery, fec.packet.Payload[:10]) + + var seqnum uint16 + for _, protectedPacket := range fec.protectedPackets { + if protectedPacket.packet != nil { + // 1. For each of the source packets that are successfully received in + // T, compute the 80-bit string by concatenating the first 64 bits + // of their RTP header and the unsigned network-ordered 16-bit + // representation of their length in bytes minus 12. + receivedHeader, err := protectedPacket.packet.Header.Marshal() + if err != nil { + return rtp.Packet{}, fmt.Errorf("marshal received header: %w", err) + } + binary.BigEndian.PutUint16(receivedHeader[2:4], uint16(protectedPacket.packet.MarshalSize()-12)) //nolint:gosec + for i := 0; i < 8; i++ { + headerRecovery[i] ^= receivedHeader[i] + } + } else { + seqnum = protectedPacket.seq + } + } + + // set version to 2 + headerRecovery[0] |= 0x80 + headerRecovery[0] &= 0xbf + payloadLength := binary.BigEndian.Uint16(headerRecovery[2:4]) + binary.BigEndian.PutUint16(headerRecovery[2:4], seqnum) + binary.BigEndian.PutUint32(headerRecovery[8:12], d.protectedStreamSSRC) + + payloadRecovery := make([]byte, payloadLength) + copy(payloadRecovery, fec.flexFec.payload) + for _, protectedPacket := range fec.protectedPackets { + if protectedPacket.packet != nil { + packet, err := protectedPacket.packet.Marshal() + if err != nil { + return rtp.Packet{}, fmt.Errorf("marshal protected packet: %w", err) + } + for i := 0; i < minInt(int(payloadLength), len(packet)-12); i++ { + payloadRecovery[i] ^= packet[12+i] + } + } + } + + headerRecovery = append(headerRecovery, payloadRecovery...) //nolint:makezero + + var packet rtp.Packet + err := packet.Unmarshal(headerRecovery) + if err != nil { + return rtp.Packet{}, fmt.Errorf("unmarshal recovered: %w", err) + } + + return packet, nil +} + +func (d *fecDecoder) discardOldRecoveredPackets() { + const limit = 192 + if len(d.recoveredPackets) > limit { + d.recoveredPackets = d.recoveredPackets[len(d.recoveredPackets)-192:] + } +} + +func decodeMask(mask uint64, bitCount uint16, seqNumBase uint16) []uint16 { + res := make([]uint16, 0) + for i := uint16(0); i < bitCount; i++ { + if (mask>>(bitCount-1-i))&1 == 1 { + res = append(res, seqNumBase+i) + } + } + + return res +} + +type fecPacketState struct { + packet rtp.Packet + flexFec flexFec + protectedPackets []*protectedPacket +} + +type flexFec struct { + protectedSSRC uint32 + seqNumBase uint16 + mask0 uint16 + mask1 uint32 + mask2 uint64 + payload []byte +} + +type protectedPacket struct { + seq uint16 + packet *rtp.Packet +} + +func parseFlexFEC03Header(data []byte) (flexFec, error) { + if len(data) < 20 { + return flexFec{}, fmt.Errorf("%w: length %d", errPacketTruncated, len(data)) + } + + rBit := (data[0] & 0x80) != 0 + if rBit { + return flexFec{}, errRetransmissionBitSet + } + + fBit := (data[0] & 0x40) != 0 + if fBit { + return flexFec{}, errInflexibleGeneratorMatrix + } + + ssrcCount := data[8] + if ssrcCount != 1 { + return flexFec{}, fmt.Errorf("%w: count %d", errMultipleSSRCProtection, ssrcCount) + } + + protectedSSRC := binary.BigEndian.Uint32(data[12:]) + seqNumBase := binary.BigEndian.Uint16(data[16:]) + rawPacketMask := data[18:] + var payload []byte + + kBit0 := (rawPacketMask[0] & 0x80) != 0 + maskPart0 := binary.BigEndian.Uint16(rawPacketMask[0:2]) & 0x7FFF + var maskPart1 uint32 + var maskPart2 uint64 + + if kBit0 { //nolint:nestif + payload = rawPacketMask[2:] + } else { + if len(data) < 24 { + return flexFec{}, fmt.Errorf("%w: length %d", errPacketTruncated, len(data)) + } + + kBit1 := (rawPacketMask[2] & 0x80) != 0 + maskPart1 = binary.BigEndian.Uint32(rawPacketMask[2:]) & 0x7FFFFFFF + + if kBit1 { + payload = rawPacketMask[6:] + } else { + if len(data) < 32 { + return flexFec{}, fmt.Errorf("%w: length %d", errPacketTruncated, len(data)) + } + + kBit2 := (rawPacketMask[6] & 0x80) != 0 + maskPart2 = binary.BigEndian.Uint64(rawPacketMask[6:]) & 0x7FFFFFFFFFFFFFFF + + if kBit2 { + payload = rawPacketMask[14:] + } else { + return flexFec{}, errLastOptionalMaskKBitSetToFalse + } + } + } + + return flexFec{ + protectedSSRC: protectedSSRC, + seqNumBase: seqNumBase, + mask0: maskPart0, + mask1: maskPart1, + mask2: maskPart2, + payload: payload, + }, nil +} + +func seqDiff(a, b uint16) uint16 { + return minUInt16(a-b, b-a) +} + +func minInt(a, b int) int { + if a < b { + return a + } + + return b +} + +func minUInt16(a, b uint16) uint16 { + if a < b { + return a + } + + return b +} + +func abs(x int) int { + if x >= 0 { + return x + } + + return -x +} + +func isNewerSeq(prevValue, value uint16) bool { + // half-way mark + breakpoint := uint16(0x8000) + if value-prevValue == breakpoint { + return value > prevValue + } + + return value != prevValue && (value-prevValue) < breakpoint +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go new file mode 100644 index 000000000..3e09551f5 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder.go @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package flexfec implements FlexFEC to recover missing RTP packets due to packet loss. +// https://datatracker.ietf.org/doc/html/rfc8627 +package flexfec + +import ( + "encoding/binary" + + "github.com/pion/interceptor/pkg/flexfec/util" + "github.com/pion/rtp" +) + +const ( + // BaseRTPHeaderSize represents the minium RTP packet header size in bytes. + BaseRTPHeaderSize = 12 + // BaseFecHeaderSize represents the minium FEC payload's header size including the + // required first mask. + BaseFecHeaderSize = 12 +) + +// EncoderFactory is an interface for generic FEC encoders. +type EncoderFactory interface { + NewEncoder(payloadType uint8, ssrc uint32) FlexEncoder +} + +// FlexEncoder is the interface that FecInterceptor uses to encode Fec packets. +type FlexEncoder interface { + EncodeFec(mediaPackets []rtp.Packet, numFecPackets uint32) []rtp.Packet +} + +// FlexEncoder20 implementation is WIP, contains bugs and no tests. Check out FlexEncoder03. +type FlexEncoder20 struct { + fecBaseSn uint16 + payloadType uint8 + ssrc uint32 + coverage *ProtectionCoverage +} + +// NewFlexEncoder returns a new FlexEncoder20. +// FlexEncoder20 implementation is WIP, contains bugs and no tests. Check out FlexEncoder03. +func NewFlexEncoder(payloadType uint8, ssrc uint32) *FlexEncoder20 { + return &FlexEncoder20{ + payloadType: payloadType, + ssrc: ssrc, + fecBaseSn: uint16(1000), + } +} + +// EncodeFec returns a list of generated RTP packets with FEC payloads that protect the specified mediaPackets. +// This method does not account for missing RTP packets in the mediaPackets array nor does it account for +// them being passed out of order. +func (flex *FlexEncoder20) EncodeFec(mediaPackets []rtp.Packet, numFecPackets uint32) []rtp.Packet { + // Start by defining which FEC packets cover which media packets + if flex.coverage == nil { + flex.coverage = NewCoverage(mediaPackets, numFecPackets) + } else { + flex.coverage.UpdateCoverage(mediaPackets, numFecPackets) + } + + if flex.coverage == nil { + return nil + } + + // Generate FEC payloads + fecPackets := make([]rtp.Packet, numFecPackets) + for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + fecPackets[fecPacketIndex] = flex.encodeFlexFecPacket(fecPacketIndex, mediaPackets[0].SequenceNumber) + } + + return fecPackets +} + +func (flex *FlexEncoder20) encodeFlexFecPacket(fecPacketIndex uint32, mediaBaseSn uint16) rtp.Packet { + mediaPacketsIt := flex.coverage.GetCoveredBy(fecPacketIndex) + flexFecHeader := flex.encodeFlexFecHeader( + mediaPacketsIt, + flex.coverage.ExtractMask1(fecPacketIndex), + flex.coverage.ExtractMask2(fecPacketIndex), + flex.coverage.ExtractMask3(fecPacketIndex), + mediaBaseSn, + ) + flexFecRepairPayload := flex.encodeFlexFecRepairPayload(mediaPacketsIt.Reset()) + + packet := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Padding: false, + Extension: false, + Marker: false, + PayloadType: flex.payloadType, + SequenceNumber: flex.fecBaseSn, + Timestamp: 54243243, + SSRC: flex.ssrc, + CSRC: []uint32{}, + }, + Payload: append(flexFecHeader, flexFecRepairPayload...), + } + flex.fecBaseSn++ + + return packet +} + +func (flex *FlexEncoder20) encodeFlexFecHeader( + mediaPackets *util.MediaPacketIterator, + mask1 uint16, + optionalMask2 uint32, + optionalMask3 uint64, + mediaBaseSn uint16, +) []byte { + /* + 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|0|P|X| CC |M| PT recovery | length recovery | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TS recovery | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SN base_i |k| Mask [0-14] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |k| Mask [15-45] (optional) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Mask [46-109] (optional) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... next SN base and Mask for CSRC_i in CSRC list ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : Repair "Payload" follows FEC Header : + : : + */ + + // Get header size - This depends on the size of the bitmask. + headerSize := BaseFecHeaderSize + if optionalMask2 > 0 { + headerSize += 4 + } + if optionalMask3 > 0 { + headerSize += 8 + } + + // Allocate the FlexFec header + flexFecHeader := make([]byte, headerSize) + + // XOR the relevant fields for the header + // TO DO - CHECK TO SEE IF THE MARSHALTO() call works with this. + tmpMediaPacketBuf := make([]byte, headerSize) + for mediaPackets.HasNext() { + mediaPacket := mediaPackets.Next() + n, err := mediaPacket.MarshalTo(tmpMediaPacketBuf) + + if n == 0 || err != nil { + return nil + } + + // XOR the first 2 bytes of the header: V, P, X, CC, M, PT fields + flexFecHeader[0] ^= tmpMediaPacketBuf[0] + flexFecHeader[1] ^= tmpMediaPacketBuf[1] + + // XOR the length recovery field + lengthRecoveryVal := uint16(mediaPacket.MarshalSize() - BaseRTPHeaderSize) //nolint:gosec // G115 + flexFecHeader[2] ^= uint8(lengthRecoveryVal >> 8) //nolint:gosec // G115 + flexFecHeader[3] ^= uint8(lengthRecoveryVal) //nolint:gosec // G115 + + // XOR the 5th to 8th bytes of the header: the timestamp field + flexFecHeader[4] ^= flexFecHeader[4] + flexFecHeader[5] ^= flexFecHeader[5] + flexFecHeader[6] ^= flexFecHeader[6] + flexFecHeader[7] ^= flexFecHeader[7] + } + + // Write the base SN for the batch of media packets + binary.BigEndian.PutUint16(flexFecHeader[8:10], mediaBaseSn) + + // Write the bitmasks to the header + binary.BigEndian.PutUint16(flexFecHeader[10:12], mask1) + + if optionalMask2 > 0 { + binary.BigEndian.PutUint32(flexFecHeader[12:16], optionalMask2) + flexFecHeader[10] |= 0b10000000 + } + if optionalMask3 > 0 { + binary.BigEndian.PutUint64(flexFecHeader[16:24], optionalMask3) + flexFecHeader[12] |= 0b10000000 + } + + return flexFecHeader +} + +func (flex *FlexEncoder20) encodeFlexFecRepairPayload(mediaPackets *util.MediaPacketIterator) []byte { + flexFecPayload := make([]byte, len(mediaPackets.First().Payload)) + + for mediaPackets.HasNext() { + mediaPacketPayload := mediaPackets.Next().Payload + + if len(flexFecPayload) < len(mediaPacketPayload) { + // Expected FEC packet payload is bigger that what we can currently store, + // we need to resize. + flexFecPayloadTmp := make([]byte, len(mediaPacketPayload)) + copy(flexFecPayloadTmp, flexFecPayload) + flexFecPayload = flexFecPayloadTmp + } + for byteIndex := 0; byteIndex < len(mediaPacketPayload); byteIndex++ { + flexFecPayload[byteIndex] ^= mediaPacketPayload[byteIndex] + } + } + + return flexFecPayload +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go new file mode 100644 index 000000000..37b905dd3 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/flexfec_encoder_03.go @@ -0,0 +1,255 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package flexfec implements FlexFEC to recover missing RTP packets due to packet loss. +// https://datatracker.ietf.org/doc/html/rfc8627 +package flexfec + +import ( + "encoding/binary" + + "github.com/pion/interceptor/pkg/flexfec/util" + "github.com/pion/rtp" +) + +const ( + // BaseFec03HeaderSize represents the minium FEC payload's header size including the + // required first mask. + BaseFec03HeaderSize = 20 +) + +// FlexEncoder03 implements the Fec encoding mechanism for the "Flex" variant of FlexFec. +type FlexEncoder03 struct { + fecBaseSn uint16 + payloadType uint8 + ssrc uint32 + coverage *ProtectionCoverage +} + +// FlexEncoder03Factory is a factory for FlexFEC-03 encoders. +type FlexEncoder03Factory struct{} + +// NewEncoder creates new FlexFEC-03 encoder. +func (f FlexEncoder03Factory) NewEncoder(payloadType uint8, ssrc uint32) FlexEncoder { + return NewFlexEncoder03(payloadType, ssrc) +} + +// NewFlexEncoder03 creates new FlexFEC-03 encoder. +func NewFlexEncoder03(payloadType uint8, ssrc uint32) *FlexEncoder03 { + return &FlexEncoder03{ + payloadType: payloadType, + ssrc: ssrc, + fecBaseSn: uint16(1000), + } +} + +// EncodeFec returns a list of generated RTP packets with FEC payloads that protect the specified mediaPackets. +// This method returns nil in case of missing RTP packets in the mediaPackets array or packets passed out of order. +func (flex *FlexEncoder03) EncodeFec(mediaPackets []rtp.Packet, numFecPackets uint32) []rtp.Packet { + // Check if mediaPackets is empty + if len(mediaPackets) == 0 { + return nil + } + + // Check if RTP packets are in order by comparing sequence numbers + for i := 1; i < len(mediaPackets); i++ { + if mediaPackets[i].SequenceNumber != mediaPackets[i-1].SequenceNumber+1 { + // Packets are not in order or there are missing packets + return nil + } + } + + // Start by defining which FEC packets cover which media packets + if flex.coverage == nil { + flex.coverage = NewCoverage(mediaPackets, numFecPackets) + } else { + flex.coverage.UpdateCoverage(mediaPackets, numFecPackets) + } + + if flex.coverage == nil { + return nil + } + + // Generate FEC payloads + fecPackets := make([]rtp.Packet, numFecPackets) + for fecPacketIndex := uint32(0); fecPacketIndex < numFecPackets; fecPacketIndex++ { + fecPackets[fecPacketIndex] = flex.encodeFlexFecPacket(fecPacketIndex, mediaPackets[0].SequenceNumber) + } + + return fecPackets +} + +func (flex *FlexEncoder03) encodeFlexFecPacket(fecPacketIndex uint32, mediaBaseSn uint16) rtp.Packet { + mediaPacketsIt := flex.coverage.GetCoveredBy(fecPacketIndex) + flexFecHeader := flex.encodeFlexFecHeader( + mediaPacketsIt, + flex.coverage.ExtractMask1(fecPacketIndex), + flex.coverage.ExtractMask2(fecPacketIndex), + flex.coverage.ExtractMask3_03(fecPacketIndex), + mediaBaseSn, + ) + flexFecRepairPayload := flex.encodeFlexFecRepairPayload(mediaPacketsIt.Reset()) + + packet := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Padding: false, + Extension: false, + Marker: false, + PayloadType: flex.payloadType, + SequenceNumber: flex.fecBaseSn, + Timestamp: 54243243, + SSRC: flex.ssrc, + CSRC: []uint32{}, + }, + Payload: append(flexFecHeader, flexFecRepairPayload...), + } + flex.fecBaseSn++ + + return packet +} + +func (flex *FlexEncoder03) encodeFlexFecHeader( //nolint:cyclop + mediaPackets *util.MediaPacketIterator, + mask1 uint16, + optionalMask2 uint32, + optionalMask3 uint64, + mediaBaseSn uint16, +) []byte { + /* + 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|0| P|X| CC |M| PT recovery | length recovery | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TS recovery | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRCCount | reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC_i | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SN base_i |k| Mask [0-14] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |k| Mask [15-45] (optional) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |k| | + +-+ Mask [46-108] (optional) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... next in SSRC_i ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + // Get header size - This depends on the size of the bitmask. + headerSize := BaseFec03HeaderSize + if optionalMask2 > 0 || optionalMask3 > 0 { + headerSize += 4 + } + if optionalMask3 > 0 { + headerSize += 8 + } + + // Allocate the FlexFec header + flexFecHeader := make([]byte, headerSize) + + // We allocate a single temporary buffer to store the mediaPacket bytes. This reduces + // overall allocations. + tmpMediaPacketBuf := make([]byte, 0) + for mediaPackets.HasNext() { + mediaPacket := mediaPackets.Next() + + if mediaPacket.MarshalSize() > len(tmpMediaPacketBuf) { + // The temporary buffer is too small, we need to resize. + tmpMediaPacketBuf = make([]byte, mediaPacket.MarshalSize()) + } + n, err := mediaPacket.MarshalTo(tmpMediaPacketBuf) + + if n == 0 || err != nil { + return nil + } + + // XOR the first 2 bytes of the header: V, P, X, CC, M, PT fields + flexFecHeader[0] ^= tmpMediaPacketBuf[0] + flexFecHeader[1] ^= tmpMediaPacketBuf[1] + + // Clear the first 2 bits + flexFecHeader[0] &= 0b00111111 + + // XOR the length recovery field + lengthRecoveryVal := uint16(mediaPacket.MarshalSize() - BaseRTPHeaderSize) //nolint:gosec // G115 + flexFecHeader[2] ^= uint8(lengthRecoveryVal >> 8) //nolint:gosec // G115 + flexFecHeader[3] ^= uint8(lengthRecoveryVal) //nolint:gosec // G115 + + // XOR the 5th to 8th bytes of the header: the timestamp field + flexFecHeader[4] ^= tmpMediaPacketBuf[4] + flexFecHeader[5] ^= tmpMediaPacketBuf[5] + flexFecHeader[6] ^= tmpMediaPacketBuf[6] + flexFecHeader[7] ^= tmpMediaPacketBuf[7] + } + + // Write the SSRC count + flexFecHeader[8] = 1 + + // Write 0s in reserved + flexFecHeader[9] = 0 + flexFecHeader[10] = 0 + flexFecHeader[11] = 0 + + // Write the SSRC of media packets protected by this FEC packet + binary.BigEndian.PutUint32(flexFecHeader[12:16], mediaPackets.First().SSRC) + + // Write the base SN for the batch of media packets + binary.BigEndian.PutUint16(flexFecHeader[16:18], mediaBaseSn) + + // Write the bitmasks to the header + binary.BigEndian.PutUint16(flexFecHeader[18:20], mask1) + + if optionalMask2 == 0 && optionalMask3 == 0 { + flexFecHeader[18] |= 0b10000000 + + return flexFecHeader + } + binary.BigEndian.PutUint32(flexFecHeader[20:24], optionalMask2) + + if optionalMask3 == 0 { + flexFecHeader[20] |= 0b10000000 + } else { + binary.BigEndian.PutUint64(flexFecHeader[24:32], optionalMask3) + flexFecHeader[24] |= 0b10000000 + } + + return flexFecHeader +} + +func (flex *FlexEncoder03) encodeFlexFecRepairPayload(mediaPackets *util.MediaPacketIterator) []byte { + flexFecPayload := make([]byte, mediaPackets.First().MarshalSize()-BaseRTPHeaderSize) + tmpMediaPacketBuf := make([]byte, 0) + + for mediaPackets.HasNext() { + mediaPacket := mediaPackets.Next() + + if mediaPacket.MarshalSize() > len(tmpMediaPacketBuf) { + tmpMediaPacketBuf = make([]byte, mediaPacket.MarshalSize()) + } + + n, err := mediaPacket.MarshalTo(tmpMediaPacketBuf) + + if n == 0 || err != nil { + return nil + } + + if len(flexFecPayload) < mediaPacket.MarshalSize()-BaseRTPHeaderSize { + // Expected FEC packet payload is bigger that what we can currently store, + // we need to resize. + flexFecPayloadTmp := make([]byte, mediaPacket.MarshalSize()-BaseRTPHeaderSize) + copy(flexFecPayloadTmp, flexFecPayload) + flexFecPayload = flexFecPayloadTmp + } + + for byteIndex := 0; byteIndex < mediaPacket.MarshalSize()-BaseRTPHeaderSize; byteIndex++ { + flexFecPayload[byteIndex] ^= tmpMediaPacketBuf[byteIndex+BaseRTPHeaderSize] + } + } + + return flexFecPayload +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/option.go new file mode 100644 index 000000000..70e61e0f9 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/option.go @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package flexfec + +// FecOption can be used to set initial options on Fec encoder interceptors. +type FecOption func(d *FecInterceptor) error + +// NumMediaPackets sets the number of media packets to accumulate before generating another FEC packets batch. +func NumMediaPackets(numMediaPackets uint32) FecOption { + return func(f *FecInterceptor) error { + f.numMediaPackets = numMediaPackets + + return nil + } +} + +// NumFECPackets sets the number of FEC packets to generate for each batch of media packets. +func NumFECPackets(numFecPackets uint32) FecOption { + return func(f *FecInterceptor) error { + f.numFecPackets = numFecPackets + + return nil + } +} + +// FECEncoderFactory sets the custom factory for constructing the FEC Encoders. +func FECEncoderFactory(factory EncoderFactory) FecOption { + return func(f *FecInterceptor) error { + f.encoderFactory = factory + + return nil + } +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/bitarray.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/bitarray.go new file mode 100644 index 000000000..0840aabf7 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/bitarray.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package util implements utilities to better support Fec decoding / encoding. +package util + +// BitArray provides support for bitmask manipulations. +type BitArray struct { + Lo uint64 // leftmost 64 bits + Hi uint64 // rightmost 64 bits +} + +// SetBit sets a bit to the specified bit value on the bitmask. +func (b *BitArray) SetBit(bitIndex uint32) { + if bitIndex < 64 { + b.Lo |= uint64(0b1) << (63 - bitIndex) + } else { + hiBitIndex := bitIndex - 64 + b.Hi |= uint64(0b1) << (63 - hiBitIndex) + } +} + +// Reset clears the bitmask. +func (b *BitArray) Reset() { + b.Lo = 0 + b.Hi = 0 +} + +// GetBit returns the bit value at a specified index of the bitmask. +func (b *BitArray) GetBit(bitIndex uint32) uint8 { + if bitIndex < 64 { + result := (b.Lo & (uint64(0b1) << (63 - bitIndex))) + if result > 0 { + return 1 + } + + return 0 + } + + hiBitIndex := bitIndex - 64 + result := (b.Hi & (uint64(0b1) << (63 - hiBitIndex))) + if result > 0 { + return 1 + } + + return 0 +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/media_packet_iterator.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/media_packet_iterator.go new file mode 100644 index 000000000..a9cc5fdc9 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/flexfec/util/media_packet_iterator.go @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package util + +import "github.com/pion/rtp" + +// MediaPacketIterator supports iterating through a list of media packets protected by +// a specific Fec packet. +type MediaPacketIterator struct { + mediaPackets []rtp.Packet + coveredIndices []uint32 + nextIndex int +} + +// NewMediaPacketIterator returns a new MediaPacketIterator. +func NewMediaPacketIterator(mediaPackets []rtp.Packet, coveredIndices []uint32) *MediaPacketIterator { + return &MediaPacketIterator{ + mediaPackets: mediaPackets, + coveredIndices: coveredIndices, + nextIndex: 0, + } +} + +// Reset sets the starting iterating index back to 0. +func (m *MediaPacketIterator) Reset() *MediaPacketIterator { + m.nextIndex = 0 + + return m +} + +// HasNext indicates whether or not there are more media packets +// that can be iterated through. +func (m *MediaPacketIterator) HasNext() bool { + return m.nextIndex < len(m.coveredIndices) +} + +// Next returns the next media packet to iterate through. +func (m *MediaPacketIterator) Next() *rtp.Packet { + if m.nextIndex == len(m.coveredIndices) { + return nil + } + packet := m.mediaPackets[m.coveredIndices[m.nextIndex]] + m.nextIndex++ + + return &packet +} + +// First returns the first media packet to iterate through. +func (m *MediaPacketIterator) First() *rtp.Packet { + if len(m.coveredIndices) == 0 { + return nil + } + + return &m.mediaPackets[m.coveredIndices[0]] +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/errors.go index b47ec39c2..8b0958d01 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/errors.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/errors.go @@ -3,13 +3,7 @@ package nack -import "errors" +import "github.com/pion/interceptor/internal/rtpbuffer" -// ErrInvalidSize is returned by newReceiveLog/newSendBuffer, when an incorrect buffer size is supplied. -var ErrInvalidSize = errors.New("invalid buffer size") - -var ( - errPacketReleased = errors.New("could not retain packet, already released") - errFailedToCastHeaderPool = errors.New("could not access header pool, failed cast") - errFailedToCastPayloadPool = errors.New("could not access payload pool, failed cast") -) +// ErrInvalidSize is returned by newReceiveLog/newRTPBuffer, when an incorrect buffer size is supplied. +var ErrInvalidSize = rtpbuffer.ErrInvalidSize 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 ab2bb2c52..10c8a01b4 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 @@ -13,14 +13,14 @@ import ( "github.com/pion/rtcp" ) -// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor +// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor. type GeneratorInterceptorFactory struct { opts []GeneratorOption } -// NewInterceptor constructs a new ReceiverInterceptor +// NewInterceptor constructs a new ReceiverInterceptor. func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &GeneratorInterceptor{ + generatorInterceptor := &GeneratorInterceptor{ streamsFilter: streamSupportNack, size: 512, skipLastN: 0, @@ -33,16 +33,16 @@ func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte } for _, opt := range g.opts { - if err := opt(i); err != nil { + if err := opt(generatorInterceptor); err != nil { return nil, err } } - if _, err := newReceiveLog(i.size); err != nil { + if _, err := newReceiveLog(generatorInterceptor.size); err != nil { return nil, err } - return i, nil + return generatorInterceptor, nil } // GeneratorInterceptor interceptor generates nack feedback messages. @@ -63,13 +63,13 @@ type GeneratorInterceptor struct { receiveLogsMu sync.Mutex } -// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory +// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory. func NewGeneratorInterceptor(opts ...GeneratorOption) (*GeneratorInterceptorFactory, error) { return &GeneratorInterceptorFactory{opts}, nil } -// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method -// will be called once per packet batch. +// 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 (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter { n.m.Lock() defer n.m.Unlock() @@ -85,9 +85,11 @@ func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) int 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 (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { +// 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 !n.streamsFilter(info) { return reader } @@ -124,7 +126,7 @@ func (n *GeneratorInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) n.receiveLogsMu.Unlock() } -// Close closes the interceptor +// Close closes the interceptor. func (n *GeneratorInterceptor) Close() error { defer n.wg.Wait() n.m.Lock() @@ -137,12 +139,15 @@ func (n *GeneratorInterceptor) Close() error { return nil } -// nolint:gocognit +// nolint:gocognit,cyclop func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { defer n.wg.Done() senderSSRC := rand.Uint32() // #nosec + missingPacketSeqNums := make([]uint16, n.size) + filteredMissingPacket := make([]uint16, n.size) + ticker := time.NewTicker(n.interval) defer ticker.Stop() for { @@ -153,7 +158,7 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { defer n.receiveLogsMu.Unlock() for ssrc, receiveLog := range n.receiveLogs { - missing := receiveLog.missingSeqNumbers(n.skipLastN) + missing := receiveLog.missingSeqNumbers(n.skipLastN, missingPacketSeqNums) if len(missing) == 0 || n.nackCountLogs[ssrc] == nil { n.nackCountLogs[ssrc] = map[uint16]uint16{} @@ -162,22 +167,33 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { continue } - filteredMissing := []uint16{} + nack := &rtcp.TransportLayerNack{} // nolint:ineffassign,wastedassign + + c := 0 // nolint:varnamelen, if n.maxNacksPerPacket > 0 { for _, missingSeq := range missing { if n.nackCountLogs[ssrc][missingSeq] < n.maxNacksPerPacket { - filteredMissing = append(filteredMissing, missingSeq) + filteredMissingPacket[c] = missingSeq + c++ } n.nackCountLogs[ssrc][missingSeq]++ } - } else { - filteredMissing = missing - } - nack := &rtcp.TransportLayerNack{ - SenderSSRC: senderSSRC, - MediaSSRC: ssrc, - Nacks: rtcp.NackPairsFromSequenceNumbers(filteredMissing), + if c == 0 { + continue + } + + nack = &rtcp.TransportLayerNack{ + SenderSSRC: senderSSRC, + MediaSSRC: ssrc, + Nacks: rtcp.NackPairsFromSequenceNumbers(filteredMissingPacket[:c]), + } + } else { + nack = &rtcp.TransportLayerNack{ + SenderSSRC: senderSSRC, + MediaSSRC: ssrc, + Nacks: rtcp.NackPairsFromSequenceNumbers(missing), + } } for nackSeq := range n.nackCountLogs[ssrc] { @@ -185,6 +201,7 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { for _, missingSeq := range missing { if missingSeq == nackSeq { isMissing = true + break } } @@ -193,10 +210,6 @@ func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { } } - if len(filteredMissing) == 0 { - continue - } - if _, err := rtcpWriter.Write([]rtcp.Packet{nack}, interceptor.Attributes{}); err != nil { n.log.Warnf("failed sending nack: %+v", err) } 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 5403e3eee..db84093ae 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 @@ -10,56 +10,63 @@ import ( "github.com/pion/logging" ) -// GeneratorOption can be used to configure GeneratorInterceptor +// GeneratorOption can be used to configure GeneratorInterceptor. type GeneratorOption func(r *GeneratorInterceptor) error // GeneratorSize sets the size of the interceptor. -// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 +// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768. func GeneratorSize(size uint16) GeneratorOption { return func(r *GeneratorInterceptor) error { r.size = size + return nil } } -// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets) to ignore when generating -// nack requests. +// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets) +// +// to ignore when generating nack requests. func GeneratorSkipLastN(skipLastN uint16) GeneratorOption { return func(r *GeneratorInterceptor) error { r.skipLastN = skipLastN + return nil } } // 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 +// 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 +// GeneratorLog sets a logger for the interceptor. func GeneratorLog(log logging.LeveledLogger) GeneratorOption { return func(r *GeneratorInterceptor) error { r.log = log + return nil } } -// GeneratorInterval sets the nack send interval for the interceptor +// GeneratorInterval sets the nack send interval for the interceptor. func GeneratorInterval(interval time.Duration) GeneratorOption { return func(r *GeneratorInterceptor) error { r.interval = interval + return nil } } -// GeneratorStreamsFilter sets filter for generator streams +// 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/receive_log.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go index 6a19996e7..c37407d12 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go @@ -6,6 +6,8 @@ package nack import ( "fmt" "sync" + + "github.com/pion/interceptor/internal/rtpbuffer" ) type receiveLog struct { @@ -23,6 +25,7 @@ func newReceiveLog(size uint16) (*receiveLog, error) { for i := 6; i < 16; i++ { if size == 1< end (with counting for rollovers) for i := s.end + 1; i != seq; i++ { // clear packets between end and seq (these may contain packets from a "size" ago) @@ -82,7 +86,7 @@ func (s *receiveLog) get(seq uint16) bool { defer s.m.RUnlock() diff := s.end - seq - if diff >= uint16SizeHalf { + if diff >= rtpbuffer.Uint16SizeHalf { return false } @@ -93,24 +97,25 @@ func (s *receiveLog) get(seq uint16) bool { return s.getReceived(seq) } -func (s *receiveLog) missingSeqNumbers(skipLastN uint16) []uint16 { +func (s *receiveLog) missingSeqNumbers(skipLastN uint16, missingPacketSeqNums []uint16) []uint16 { s.m.RLock() defer s.m.RUnlock() until := s.end - skipLastN - if until-s.lastConsecutive >= uint16SizeHalf { + if until-s.lastConsecutive >= rtpbuffer.Uint16SizeHalf { // until < s.lastConsecutive (counting for rollover) return nil } - missingPacketSeqNums := make([]uint16, 0) + c := 0 for i := s.lastConsecutive + 1; i != until+1; i++ { if !s.getReceived(i) { - missingPacketSeqNums = append(missingPacketSeqNums, i) + missingPacketSeqNums[c] = i + c++ } } - return missingPacketSeqNums + return missingPacketSeqNums[:c] } func (s *receiveLog) setReceived(seq uint16) { @@ -125,6 +130,7 @@ func (s *receiveLog) delReceived(seq uint16) { func (s *receiveLog) getReceived(seq uint16) bool { pos := seq % s.size + return (s.packets[pos/64] & (1 << (pos % 64))) != 0 } 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 22d038ba4..8b5585ac9 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 @@ -7,23 +7,20 @@ import ( "sync" "github.com/pion/interceptor" + "github.com/pion/interceptor/internal/rtpbuffer" "github.com/pion/logging" "github.com/pion/rtcp" "github.com/pion/rtp" ) -// ResponderInterceptorFactory is a interceptor.Factory for a ResponderInterceptor +// ResponderInterceptorFactory is a interceptor.Factory for a ResponderInterceptor. type ResponderInterceptorFactory struct { opts []ResponderOption } -type packetFactory interface { - NewPacket(header *rtp.Header, payload []byte, rtxSsrc uint32, rtxPayloadType uint8) (*retainablePacket, error) -} - -// NewInterceptor constructs a new ResponderInterceptor +// NewInterceptor constructs a new ResponderInterceptor. func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &ResponderInterceptor{ + responderInterceptor := &ResponderInterceptor{ streamsFilter: streamSupportNack, size: 1024, log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"), @@ -31,40 +28,41 @@ func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Inte } for _, opt := range r.opts { - if err := opt(i); err != nil { + if err := opt(responderInterceptor); err != nil { return nil, err } } - if i.packetFactory == nil { - i.packetFactory = newPacketManager() + if responderInterceptor.packetFactory == nil { + responderInterceptor.packetFactory = rtpbuffer.NewPacketFactoryCopy() } - if _, err := newSendBuffer(i.size); err != nil { + if _, err := rtpbuffer.NewRTPBuffer(responderInterceptor.size); err != nil { return nil, err } - return i, nil + return responderInterceptor, nil } -// ResponderInterceptor responds to nack feedback messages +// ResponderInterceptor responds to nack feedback messages. type ResponderInterceptor struct { interceptor.NoOp streamsFilter func(info *interceptor.StreamInfo) bool size uint16 log logging.LeveledLogger - packetFactory packetFactory + packetFactory rtpbuffer.PacketFactory streams map[uint32]*localStream streamsMu sync.Mutex } type localStream struct { - sendBuffer *sendBuffer - rtpWriter interceptor.RTPWriter + rtpBuffer *rtpbuffer.RTPBuffer + rtpBufferMutex sync.RWMutex + rtpWriter interceptor.RTPWriter } -// NewResponderInterceptor returns a new ResponderInterceptorFactor +// NewResponderInterceptor returns a new ResponderInterceptorFactor. func NewResponderInterceptor(opts ...ResponderOption) (*ResponderInterceptorFactory, error) { return &ResponderInterceptorFactory{opts}, nil } @@ -98,30 +96,44 @@ 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 { +// 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 !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, + rtpBuffer, _ := rtpbuffer.NewRTPBuffer(n.size) + stream := &localStream{ + rtpBuffer: rtpBuffer, + rtpWriter: writer, } + n.streamsMu.Lock() + n.streams[info.SSRC] = stream n.streamsMu.Unlock() - return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { - pkt, err := n.packetFactory.NewPacket(header, payload, info.SSRCRetransmission, info.PayloadTypeRetransmission) - if err != nil { - return 0, err - } - sendBuffer.add(pkt) - return writer.Write(header, payload, attributes) - }) + return interceptor.RTPWriterFunc( + func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { + // If this packet doesn't belong to the main SSRC, do not add it to rtpBuffer + if header.SSRC != info.SSRC { + return writer.Write(header, payload, attributes) + } + + pkt, err := n.packetFactory.NewPacket(header, payload, info.SSRCRetransmission, info.PayloadTypeRetransmission) + if err != nil { + return 0, err + } + stream.rtpBufferMutex.Lock() + defer stream.rtpBufferMutex.Unlock() + + rtpBuffer.Add(pkt) + + return writer.Write(header, payload, attributes) + }, + ) } // UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track. @@ -141,7 +153,10 @@ func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) { for i := range nack.Nacks { nack.Nacks[i].Range(func(seq uint16) bool { - if p := stream.sendBuffer.get(seq); p != nil { + stream.rtpBufferMutex.Lock() + defer stream.rtpBufferMutex.Unlock() + + if p := stream.rtpBuffer.Get(seq); p != nil { if _, err := stream.rtpWriter.Write(p.Header(), p.Payload(), interceptor.Attributes{}); err != nil { n.log.Warnf("failed resending nacked packet: %+v", 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 24c7c4693..ea9435810 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 @@ -5,42 +5,47 @@ package nack import ( "github.com/pion/interceptor" + "github.com/pion/interceptor/internal/rtpbuffer" "github.com/pion/logging" ) -// ResponderOption can be used to configure ResponderInterceptor +// ResponderOption can be used to configure ResponderInterceptor. type ResponderOption func(s *ResponderInterceptor) error // ResponderSize sets the size of the interceptor. -// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 +// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768. func ResponderSize(size uint16) ResponderOption { return func(r *ResponderInterceptor) error { r.size = size + return nil } } -// ResponderLog sets a logger for the interceptor +// ResponderLog sets a logger for the interceptor. func ResponderLog(log logging.LeveledLogger) ResponderOption { return func(r *ResponderInterceptor) error { r.log = log + return nil } } // DisableCopy bypasses copy of underlying packets. It should be used when -// you are not re-using underlying buffers of packets that have been written +// you are not re-using underlying buffers of packets that have been written. func DisableCopy() ResponderOption { return func(s *ResponderInterceptor) error { - s.packetFactory = &noOpPacketFactory{} + s.packetFactory = &rtpbuffer.PacketFactoryNoOp{} + return nil } } -// ResponderStreamsFilter sets filter for local streams +// 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 deleted file mode 100644 index 18c533a8a..000000000 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package nack - -import ( - "encoding/binary" - "io" - "sync" - - "github.com/pion/rtp" -) - -const maxPayloadLen = 1460 - -type packetManager struct { - headerPool *sync.Pool - payloadPool *sync.Pool - rtxSequencer rtp.Sequencer -} - -func newPacketManager() *packetManager { - return &packetManager{ - headerPool: &sync.Pool{ - New: func() interface{} { - return &rtp.Header{} - }, - }, - payloadPool: &sync.Pool{ - New: func() interface{} { - buf := make([]byte, maxPayloadLen) - return &buf - }, - }, - rtxSequencer: rtp.NewRandomSequencer(), - } -} - -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, - sequenceNumber: header.SequenceNumber, - // new packets have retain count of 1 - count: 1, - } - - var ok bool - p.header, ok = m.headerPool.Get().(*rtp.Header) - if !ok { - return nil, errFailedToCastHeaderPool - } - - *p.header = header.Clone() - - if payload != nil { - p.buffer, ok = m.payloadPool.Get().(*[]byte) - if !ok { - return nil, errFailedToCastPayloadPool - } - - size := copy(*p.buffer, payload) - 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 -} - -func (m *packetManager) releasePacket(header *rtp.Header, payload *[]byte) { - m.headerPool.Put(header) - if payload != nil { - m.payloadPool.Put(payload) - } -} - -type noOpPacketFactory struct{} - -func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte, _ uint32, _ uint8) (*retainablePacket, error) { - return &retainablePacket{ - onRelease: f.releasePacket, - count: 1, - header: header, - payload: payload, - sequenceNumber: header.SequenceNumber, - }, nil -} - -func (f *noOpPacketFactory) releasePacket(_ *rtp.Header, _ *[]byte) { - // no-op -} - -type retainablePacket struct { - onRelease func(*rtp.Header, *[]byte) - - countMu sync.Mutex - count int - - header *rtp.Header - buffer *[]byte - payload []byte - - sequenceNumber uint16 -} - -func (p *retainablePacket) Header() *rtp.Header { - return p.header -} - -func (p *retainablePacket) Payload() []byte { - return p.payload -} - -func (p *retainablePacket) Retain() error { - p.countMu.Lock() - defer p.countMu.Unlock() - if p.count == 0 { - // already released - return errPacketReleased - } - p.count++ - return nil -} - -func (p *retainablePacket) Release() { - p.countMu.Lock() - defer p.countMu.Unlock() - p.count-- - - if p.count == 0 { - // release back to pool - p.onRelease(p.header, p.buffer) - p.header = nil - p.buffer = nil - p.payload = nil - } -} 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 deleted file mode 100644 index 2b3b076f5..000000000 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package nack - -import ( - "fmt" - "sync" -) - -const ( - uint16SizeHalf = 1 << 15 -) - -type sendBuffer struct { - packets []*retainablePacket - size uint16 - lastAdded uint16 - started bool - - m sync.RWMutex -} - -func newSendBuffer(size uint16) (*sendBuffer, error) { - allowedSizes := make([]uint16, 0) - correctSize := false - for i := 0; i < 16; i++ { - if size == 1<= uint16SizeHalf { - return nil - } - - if diff >= s.size { - return nil - } - - pkt := s.packets[seq%s.size] - if pkt != nil { - if pkt.sequenceNumber != seq { - return nil - } - // already released - if err := pkt.Retain(); err != nil { - return nil - } - } - return pkt -} 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 0afbd08f5..91b513c55 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 @@ -12,14 +12,14 @@ import ( "github.com/pion/rtcp" ) -// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor +// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor. type ReceiverInterceptorFactory struct { opts []ReceiverOption } -// NewInterceptor constructs a new ReceiverInterceptor +// NewInterceptor constructs a new ReceiverInterceptor. func (r *ReceiverInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &ReceiverInterceptor{ + receiverInterceptor := &ReceiverInterceptor{ interval: 1 * time.Second, now: time.Now, log: logging.NewDefaultLoggerFactory().NewLogger("receiver_interceptor"), @@ -27,15 +27,15 @@ func (r *ReceiverInterceptorFactory) NewInterceptor(_ string) (interceptor.Inter } for _, opt := range r.opts { - if err := opt(i); err != nil { + if err := opt(receiverInterceptor); err != nil { return nil, err } } - return i, nil + return receiverInterceptor, nil } -// NewReceiverInterceptor returns a new ReceiverInterceptorFactory +// NewReceiverInterceptor returns a new ReceiverInterceptorFactory. func NewReceiverInterceptor(opts ...ReceiverOption) (*ReceiverInterceptorFactory, error) { return &ReceiverInterceptorFactory{opts}, nil } @@ -103,7 +103,9 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { 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 { + } else if _, err := rtcpWriter.Write( + []rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}, + ); err != nil { r.log.Warnf("failed sending: %+v", err) } @@ -116,9 +118,11 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { } } -// 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 (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { +// 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 (r *ReceiverInterceptor) BindRemoteStream( + info *interceptor.StreamInfo, reader interceptor.RTPReader, +) interceptor.RTPReader { stream := newReceiverStream(info.SSRC, info.ClockRate) r.streams.Store(info.SSRC, stream) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go index 337a34142..4a91561d9 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go @@ -16,6 +16,7 @@ type ReceiverOption func(r *ReceiverInterceptor) error func ReceiverLog(log logging.LeveledLogger) ReceiverOption { return func(r *ReceiverInterceptor) error { r.log = log + return nil } } @@ -24,6 +25,7 @@ func ReceiverLog(log logging.LeveledLogger) ReceiverOption { func ReceiverInterval(interval time.Duration) ReceiverOption { return func(r *ReceiverInterceptor) error { r.interval = interval + return nil } } @@ -32,6 +34,7 @@ func ReceiverInterval(interval time.Duration) ReceiverOption { func ReceiverNow(f func() time.Time) ReceiverOption { return func(r *ReceiverInterceptor) error { r.now = f + return nil } } 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 ebe08473d..92913538b 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 @@ -41,6 +41,7 @@ type receiverStream struct { func newReceiverStream(ssrc uint32, clockRate uint32) *receiverStream { receiverSSRC := rand.Uint32() // #nosec + return &receiverStream{ ssrc: ssrc, receiverSSRC: receiverSSRC, @@ -54,6 +55,7 @@ func (stream *receiverStream) processRTP(now time.Time, pktHeader *rtp.Header) { stream.m.Lock() defer stream.m.Unlock() + //nolint:nestif if !stream.started { // first frame stream.started = true stream.setReceived(pktHeader.SequenceNumber) @@ -104,6 +106,7 @@ func (stream *receiverStream) delReceived(seq uint16) { func (stream *receiverStream) getReceived(seq uint16) bool { pos := seq % (stream.size * packetsPerHistoryEntry) + return (stream.packets[pos/packetsPerHistoryEntry] & (1 << (pos % packetsPerHistoryEntry))) != 0 } @@ -111,7 +114,7 @@ func (stream *receiverStream) processSenderReport(now time.Time, sr *rtcp.Sender stream.m.Lock() defer stream.m.Unlock() - stream.lastSenderReport = uint32(sr.NTPTime >> 16) + stream.lastSenderReport = uint32(sr.NTPTime >> 16) //nolint:gosec // G115 stream.lastSenderReportTime = now } @@ -131,6 +134,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport ret++ } } + return ret }() stream.totalLost += totalLostSinceReport @@ -143,7 +147,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport stream.totalLost = 0xFFFFFF } - r := &rtcp.ReceiverReport{ + receiverReport := &rtcp.ReceiverReport{ SSRC: stream.receiverSSRC, Reports: []rtcp.ReceptionReport{ { @@ -156,6 +160,7 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport if stream.lastSenderReportTime.IsZero() { return 0 } + return uint32(now.Sub(stream.lastSenderReportTime).Seconds() * 65536) }(), Jitter: uint32(stream.jitter), @@ -165,5 +170,5 @@ func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport stream.lastReportSeqnum = stream.lastSeqnum - return r + return receiverReport } 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 1b6f4b252..40c7f9f7a 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,17 +13,17 @@ import ( "github.com/pion/rtp" ) -// TickerFactory is a factory to create new tickers +// TickerFactory is a factory to create new tickers. type TickerFactory func(d time.Duration) Ticker -// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor +// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor. type SenderInterceptorFactory struct { opts []SenderOption } -// NewInterceptor constructs a new SenderInterceptor +// NewInterceptor constructs a new SenderInterceptor. func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &SenderInterceptor{ + senderInterceptor := &SenderInterceptor{ interval: 1 * time.Second, now: time.Now, newTicker: func(d time.Duration) Ticker { @@ -34,15 +34,15 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce } for _, opt := range s.opts { - if err := opt(i); err != nil { + if err := opt(senderInterceptor); err != nil { return nil, err } } - return i, nil + return senderInterceptor, nil } -// NewSenderInterceptor returns a new SenderInterceptorFactory +// NewSenderInterceptor returns a new SenderInterceptorFactory. func NewSenderInterceptor(opts ...SenderOption) (*SenderInterceptorFactory, error) { return &SenderInterceptorFactory{opts}, nil } @@ -119,7 +119,9 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { 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 { + } else if _, err := rtcpWriter.Write( + []rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}, + ); err != nil { s.log.Warnf("failed sending: %+v", err) } @@ -134,7 +136,9 @@ 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 { +func (s *SenderInterceptor) BindLocalStream( + info *interceptor.StreamInfo, writer interceptor.RTPWriter, +) interceptor.RTPWriter { stream := newSenderStream(info.SSRC, info.ClockRate, s.useLatestPacket) s.streams.Store(info.SSRC, stream) 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 1e489b0b3..07a4f0875 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 @@ -16,6 +16,7 @@ type SenderOption func(r *SenderInterceptor) error func SenderLog(log logging.LeveledLogger) SenderOption { return func(r *SenderInterceptor) error { r.log = log + return nil } } @@ -24,6 +25,7 @@ func SenderLog(log logging.LeveledLogger) SenderOption { func SenderInterval(interval time.Duration) SenderOption { return func(r *SenderInterceptor) error { r.interval = interval + return nil } } @@ -32,6 +34,7 @@ func SenderInterval(interval time.Duration) SenderOption { func SenderNow(f func() time.Time) SenderOption { return func(r *SenderInterceptor) error { r.now = f + return nil } } @@ -40,6 +43,7 @@ func SenderNow(f func() time.Time) SenderOption { func SenderTicker(f TickerFactory) SenderOption { return func(r *SenderInterceptor) error { r.newTicker = f + return nil } } @@ -49,6 +53,7 @@ func SenderTicker(f TickerFactory) SenderOption { func SenderUseLatestPacket() SenderOption { return func(r *SenderInterceptor) error { r.useLatestPacket = true + return nil } } @@ -58,6 +63,7 @@ func SenderUseLatestPacket() SenderOption { 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 44658e246..6bb37dbff 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 @@ -48,7 +48,7 @@ func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payloa } stream.packetCount++ - stream.octetCount += uint32(len(payload)) + stream.octetCount += uint32(len(payload)) //nolint:gosec // G115 } func (stream *senderStream) generateReport(now time.Time) *rtcp.SenderReport { 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 index efe2df29c..344ec1be1 100644 --- 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 @@ -14,17 +14,17 @@ import ( "github.com/pion/rtcp" ) -// TickerFactory is a factory to create new tickers +// TickerFactory is a factory to create new tickers. type TickerFactory func(d time.Duration) ticker -// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor +// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor. type SenderInterceptorFactory struct { opts []Option } -// NewInterceptor constructs a new SenderInterceptor +// NewInterceptor constructs a new SenderInterceptor. func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &SenderInterceptor{ + senderInterceptor := &SenderInterceptor{ NoOp: interceptor.NoOp{}, log: logging.NewDefaultLoggerFactory().NewLogger("rfc8888_interceptor"), lock: sync.Mutex{}, @@ -40,12 +40,13 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce close: make(chan struct{}), } for _, opt := range s.opts { - err := opt(i) + err := opt(senderInterceptor) if err != nil { return nil, err } } - return i, nil + + return senderInterceptor, nil } // NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options. @@ -91,9 +92,12 @@ func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interc 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 { +// 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 { @@ -115,6 +119,7 @@ func (s *SenderInterceptor) BindRemoteStream(_ *interceptor.StreamInfo, reader i ecn: 0, // ECN is not supported (yet). } s.packetChan <- p + return i, attr, nil }) } @@ -157,16 +162,19 @@ func (s *SenderInterceptor) loop(writer interceptor.RTCPWriter) { 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(): + case <-t.Ch(): + now := s.now() 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)) 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 index 1214868bf..236073b5d 100644 --- 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 @@ -5,13 +5,14 @@ package rfc8888 import "time" -// An Option is a function that can be used to configure a SenderInterceptor +// 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 } } @@ -20,14 +21,16 @@ func SenderTicker(f TickerFactory) Option { 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 +// 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 index 4423df1e3..a1a2cf266 100644 --- 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 @@ -6,6 +6,7 @@ package rfc8888 import ( "time" + "github.com/pion/interceptor/internal/ntp" "github.com/pion/rtcp" ) @@ -21,7 +22,7 @@ type Recorder struct { streams map[uint32]*streamLog } -// NewRecorder creates a new Recorder +// NewRecorder creates a new Recorder. func NewRecorder() *Recorder { return &Recorder{ streams: map[uint32]*streamLog{}, @@ -44,7 +45,7 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor report := &rtcp.CCFeedbackReport{ SenderSSRC: r.ssrc, ReportBlocks: []rtcp.CCFeedbackReportBlock{}, - ReportTimestamp: ntpTime32(now), + ReportTimestamp: ntp.ToNTP32(now), } maxReportBlocks := (maxSize - 12 - (8 * len(r.streams))) / 2 @@ -65,14 +66,3 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor 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 index 8dfb14fc9..ed41f9528 100644 --- 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 @@ -6,6 +6,7 @@ package rfc8888 import ( "time" + "github.com/pion/interceptor/internal/sequencenumber" "github.com/pion/rtcp" ) @@ -13,7 +14,7 @@ const maxReportsPerReportBlock = 16384 type streamLog struct { ssrc uint32 - sequence unwrapper + sequence sequencenumber.Unwrapper init bool nextSequenceNumberToReport int64 // next to report lastSequenceNumberReceived int64 // highest received @@ -23,7 +24,7 @@ type streamLog struct { func newStreamLog(ssrc uint32) *streamLog { return &streamLog{ ssrc: ssrc, - sequence: unwrapper{}, + sequence: sequencenumber.Unwrapper{}, init: false, nextSequenceNumberToReport: 0, lastSequenceNumberReceived: 0, @@ -32,7 +33,7 @@ func newStreamLog(ssrc uint32) *streamLog { } func (l *streamLog) add(ts time.Time, sequenceNumber uint16, ecn uint8) { - unwrappedSequenceNumber := l.sequence.unwrap(sequenceNumber) + unwrappedSequenceNumber := l.sequence.Unwrap(sequenceNumber) if !l.init { l.init = true l.nextSequenceNumberToReport = unwrappedSequenceNumber @@ -52,7 +53,7 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc if len(l.log) == 0 { return rtcp.CCFeedbackReportBlock{ MediaSSRC: l.ssrc, - BeginSequence: uint16(l.nextSequenceNumberToReport), + BeginSequence: uint16(l.nextSequenceNumberToReport), //nolint:gosec // G115 MetricBlocks: []rtcp.CCFeedbackMetricBlock{}, } } @@ -65,7 +66,7 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc offset := l.nextSequenceNumberToReport lastReceived := l.nextSequenceNumberToReport gapDetected := false - for i := offset; i <= l.lastSequenceNumberReceived; i++ { + for i := offset; i <= l.lastSequenceNumberReceived; i++ { //nolint:varnamelen // i int64 received := false ecn := uint8(0) ato := uint16(0) @@ -91,9 +92,10 @@ func (l *streamLog) metricsAfter(reference time.Time, maxReportBlocks int64) rtc } } } + return rtcp.CCFeedbackReportBlock{ MediaSSRC: l.ssrc, - BeginSequence: uint16(offset), + BeginSequence: uint16(offset), //nolint:gosec // G115 MetricBlocks: metricBlocks, } } @@ -106,5 +108,6 @@ func getArrivalTimeOffset(base time.Time, arrival time.Time) uint16 { if ato > 0x1FFD { return 0x1FFE } + return ato } 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 deleted file mode 100644 index f15f33e6e..000000000 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/rfc8888/unwrapper.go +++ /dev/null @@ -1,42 +0,0 @@ -// 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 index d3e2a8af3..a496ad73c 100644 --- 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 @@ -12,6 +12,8 @@ const ( // 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 +// +//nolint:lll type packetArrivalTimeMap struct { // arrivalTimes is a circular buffer, where the packet with sequence number sn is stored // in slot sn % len(arrivalTimes) @@ -31,12 +33,14 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64 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 } @@ -53,6 +57,7 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64 m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime m.setNotReceived(sequenceNumber+1, m.beginSequenceNumber) m.beginSequenceNumber = sequenceNumber + return } @@ -64,6 +69,7 @@ func (m *packetArrivalTimeMap) AddPacket(sequenceNumber int64, arrivalTime int64 m.beginSequenceNumber = sequenceNumber m.endSequenceNumber = newEndSequenceNumber m.arrivalTimes[m.index(sequenceNumber)] = arrivalTime + return } @@ -99,12 +105,15 @@ func (m *packetArrivalTimeMap) EndSequenceNumber() int64 { // 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) { +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 } @@ -116,6 +125,7 @@ func (m *packetArrivalTimeMap) EraseTo(sequenceNumber int64) { if sequenceNumber >= m.endSequenceNumber { // Erase all. m.beginSequenceNumber = m.endSequenceNumber + return } // Remove some @@ -138,7 +148,7 @@ func (m *packetArrivalTimeMap) HasReceived(sequenceNumber int64) bool { return m.get(sequenceNumber) >= 0 } -// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber] +// Clamp returns sequenceNumber clamped to [beginSequenceNumber, endSequenceNumber]. func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 { if sequenceNumber < m.beginSequenceNumber { return m.beginSequenceNumber @@ -146,6 +156,7 @@ func (m *packetArrivalTimeMap) Clamp(sequenceNumber int64) int64 { if m.endSequenceNumber < sequenceNumber { return m.endSequenceNumber } + return sequenceNumber } @@ -153,6 +164,7 @@ func (m *packetArrivalTimeMap) get(sequenceNumber int64) int64 { if sequenceNumber < m.beginSequenceNumber || sequenceNumber >= m.endSequenceNumber { return -1 } + return m.arrivalTimes[m.index(sequenceNumber)] } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go index 791b14592..52d9e84aa 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go @@ -13,20 +13,20 @@ import ( var errHeaderIsNil = errors.New("header is nil") -// HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor +// HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor. type HeaderExtensionInterceptorFactory struct{} -// NewInterceptor constructs a new HeaderExtensionInterceptor +// NewInterceptor constructs a new HeaderExtensionInterceptor. func (h *HeaderExtensionInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { return &HeaderExtensionInterceptor{}, nil } -// NewHeaderExtensionInterceptor returns a HeaderExtensionInterceptorFactory +// NewHeaderExtensionInterceptor returns a HeaderExtensionInterceptorFactory. func NewHeaderExtensionInterceptor() (*HeaderExtensionInterceptorFactory, error) { return &HeaderExtensionInterceptorFactory{}, nil } -// HeaderExtensionInterceptor adds transport wide sequence numbers as header extension to each RTP packet +// HeaderExtensionInterceptor adds transport wide sequence numbers as header extension to each RTP packet. type HeaderExtensionInterceptor struct { interceptor.NoOp nextSequenceNr uint32 @@ -36,31 +36,39 @@ const transportCCURI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide // BindLocalStream returns a writer that adds a rtp.TransportCCExtension // header with increasing sequence numbers to each outgoing packet. -func (h *HeaderExtensionInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter { +func (h *HeaderExtensionInterceptor) BindLocalStream( + info *interceptor.StreamInfo, + writer interceptor.RTPWriter, +) interceptor.RTPWriter { var hdrExtID uint8 for _, e := range info.RTPHeaderExtensions { if e.URI == transportCCURI { - hdrExtID = uint8(e.ID) + hdrExtID = uint8(e.ID) //nolint:gosec // G115 + break } } if hdrExtID == 0 { // Don't add header extension if ID is 0, because 0 is an invalid extension ID return writer } - return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { - sequenceNumber := atomic.AddUint32(&h.nextSequenceNr, 1) - 1 - tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(sequenceNumber)}).Marshal() - if err != nil { - return 0, err - } - if header == nil { - return 0, errHeaderIsNil - } - err = header.SetExtension(hdrExtID, tcc) - if err != nil { - return 0, err - } - return writer.Write(header, payload, attributes) - }) + return interceptor.RTPWriterFunc( + func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { + sequenceNumber := atomic.AddUint32(&h.nextSequenceNr, 1) - 1 + //nolint:gosec // G115 + tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(sequenceNumber)}).Marshal() + if err != nil { + return 0, err + } + if header == nil { + return 0, errHeaderIsNil + } + err = header.SetExtension(hdrExtID, tcc) + if err != nil { + return 0, err + } + + return writer.Write(header, payload, attributes) + }, + ) } 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 d7906fc68..229330ad4 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 @@ -14,16 +14,16 @@ import ( "github.com/pion/rtp" ) -// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor +// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor. type SenderInterceptorFactory struct { opts []Option } var errClosed = errors.New("interceptor is closed") -// NewInterceptor constructs a new SenderInterceptor +// NewInterceptor constructs a new SenderInterceptor. func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { - i := &SenderInterceptor{ + senderInterceptor := &SenderInterceptor{ log: logging.NewDefaultLoggerFactory().NewLogger("twcc_sender_interceptor"), packetChan: make(chan packet), close: make(chan struct{}), @@ -32,13 +32,13 @@ func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interce } for _, opt := range s.opts { - err := opt(i) + err := opt(senderInterceptor) if err != nil { return nil, err } } - return i, nil + return senderInterceptor, nil } // NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options. @@ -64,7 +64,7 @@ type SenderInterceptor struct { packetChan chan packet } -// An Option is a function that can be used to configure a SenderInterceptor +// An Option is a function that can be used to configure a SenderInterceptor. type Option func(*SenderInterceptor) error // SendInterval sets the interval at which the interceptor @@ -72,6 +72,7 @@ type Option func(*SenderInterceptor) error func SendInterval(interval time.Duration) Option { return func(s *SenderInterceptor) error { s.interval = interval + return nil } } @@ -102,54 +103,63 @@ type packet struct { ssrc uint32 } -// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method +// 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(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { +// +//nolint:cyclop +func (s *SenderInterceptor) BindRemoteStream( + info *interceptor.StreamInfo, reader interceptor.RTPReader, +) interceptor.RTPReader { var hdrExtID uint8 for _, e := range info.RTPHeaderExtensions { if e.URI == transportCCURI { - hdrExtID = uint8(e.ID) + hdrExtID = uint8(e.ID) //nolint:gosec // G115 + break } } if hdrExtID == 0 { // Don't try to read header extension if ID is 0, because 0 is an invalid extension ID return reader } - return interceptor.RTPReaderFunc(func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) { - i, attr, err := reader.Read(buf, attributes) - if err != nil { - return 0, nil, err - } - if attr == nil { - attr = make(interceptor.Attributes) - } - header, err := attr.GetRTPHeader(buf[:i]) - if err != nil { - return 0, nil, err - } - var tccExt rtp.TransportCCExtension - if ext := header.GetExtension(hdrExtID); ext != nil { - err = tccExt.Unmarshal(ext) + return interceptor.RTPReaderFunc( + func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) { + i, attr, err := reader.Read(buf, attributes) if err != nil { return 0, nil, err } - p := packet{ - hdr: header, - sequenceNumber: tccExt.TransportSequence, - arrivalTime: time.Since(s.startTime).Microseconds(), - ssrc: info.SSRC, + if attr == nil { + attr = make(interceptor.Attributes) } - select { - case <-s.close: - return 0, nil, errClosed - case s.packetChan <- p: + header, err := attr.GetRTPHeader(buf[:i]) + if err != nil { + return 0, nil, err } - } + var tccExt rtp.TransportCCExtension + if ext := header.GetExtension(hdrExtID); ext != nil { + err = tccExt.Unmarshal(ext) + if err != nil { + return 0, nil, err + } - return i, attr, nil - }) + p := packet{ + hdr: header, + sequenceNumber: tccExt.TransportSequence, + arrivalTime: time.Since(s.startTime).Microseconds(), + ssrc: info.SSRC, + } + select { + case <-s.close: + return 0, nil, errClosed + case s.packetChan <- p: + } + } + + return i, attr, nil + }, + ) } // Close closes the interceptor. @@ -174,7 +184,7 @@ func (s *SenderInterceptor) isClosed() bool { } } -func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) { +func (s *SenderInterceptor) loop(writer interceptor.RTCPWriter) { defer s.wg.Done() select { @@ -189,6 +199,7 @@ func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) { select { case <-s.close: ticker.Stop() + return case p := <-s.packetChan: s.recorder.Record(p.ssrc, p.sequenceNumber, p.arrivalTime) @@ -199,7 +210,7 @@ func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) { if len(pkts) == 0 { continue } - if _, err := w.Write(pkts, nil); err != nil { + if _, err := writer.Write(pkts, nil); err != nil { s.log.Error(err.Error()) } } 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 6464c77bc..750e4541d 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 @@ -71,12 +71,13 @@ func (r *Recorder) Record(mediaSSRC uint32, sequenceNumber uint16, arrivalTime i } func (r *Recorder) maybeCullOldPackets(sequenceNumber int64, arrivalTime int64) { - if r.startSequenceNumber != nil && *r.startSequenceNumber >= r.arrivalTimeMap.EndSequenceNumber() && arrivalTime >= packetWindowMicroseconds { + if r.startSequenceNumber != nil && *r.startSequenceNumber >= r.arrivalTimeMap.EndSequenceNumber() && + arrivalTime >= packetWindowMicroseconds { r.arrivalTimeMap.RemoveOldPackets(sequenceNumber, arrivalTime-packetWindowMicroseconds) } } -// PacketsHeld returns the number of received packets currently held by the recorder +// PacketsHeld returns the number of received packets currently held by the recorder. func (r *Recorder) PacketsHeld() int { return r.packetsHeld } @@ -101,6 +102,7 @@ func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet { // old. } r.packetsHeld = 0 + return feedbacks } @@ -109,6 +111,7 @@ func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet { 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 + //nolint:lll 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 @@ -136,18 +139,19 @@ func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclu // 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) + fb.setBase(uint16(baseSequenceNumber), arrivalTime) //nolint:gosec // G115 - if !fb.addReceived(uint16(seq), arrivalTime) { + if !fb.addReceived(uint16(seq), arrivalTime) { //nolint:gosec // G115 // 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) { + } else if !fb.addReceived(uint16(seq), arrivalTime) { //nolint:gosec // G115 // Could not add timestamp. Packet may be full. Return // and try again with a fresh packet. break @@ -157,6 +161,7 @@ func (r *Recorder) maybeBuildFeedbackPacket(beginSeqNumInclusive, endSeqNumExclu } r.startSequenceNumber = &nextSequenceNumber + return fb } @@ -192,7 +197,7 @@ func (f *feedback) setBase(sequenceNumber uint16, timeUS int64) { func (f *feedback) getRTCP() *rtcp.TransportLayerCC { f.rtcp.PacketStatusCount = f.sequenceNumberCount - f.rtcp.ReferenceTime = uint32(f.refTimestamp64MS) + f.rtcp.ReferenceTime = uint32(f.refTimestamp64MS) //nolint:gosec // G115 f.rtcp.BaseSequenceNumber = f.baseSequenceNumber for len(f.lastChunk.deltas) > 0 { f.chunks = append(f.chunks, f.lastChunk.encode()) @@ -200,7 +205,8 @@ func (f *feedback) getRTCP() *rtcp.TransportLayerCC { f.rtcp.PacketChunks = append(f.rtcp.PacketChunks, f.chunks...) f.rtcp.RecvDeltas = f.deltas - padLen := 20 + len(f.rtcp.PacketChunks)*2 + f.len // 4 bytes header + 16 bytes twcc header + 2 bytes for each chunk + length of deltas + // 4 bytes header + 16 bytes twcc header + 2 bytes for each chunk + length of deltas + padLen := 20 + len(f.rtcp.PacketChunks)*2 + f.len padding := padLen%4 != 0 for padLen%4 != 0 { padLen++ @@ -209,7 +215,7 @@ func (f *feedback) getRTCP() *rtcp.TransportLayerCC { Count: rtcp.FormatTCC, Type: rtcp.TypeTransportSpecificFeedback, Padding: padding, - Length: uint16((padLen / 4) - 1), + Length: uint16((padLen / 4) - 1), //nolint:gosec // G115 } return f.rtcp @@ -223,7 +229,8 @@ func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool { } 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 + // delta doesn't fit into 16 bit, need to create new packet + if delta250US < math.MinInt16 || delta250US > math.MaxInt16 { return false } deltaUSRounded := delta250US * rtcp.TypeTCCDeltaScaleFactor @@ -257,6 +264,7 @@ func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool { f.lastTimestampUS += deltaUSRounded f.sequenceNumberCount++ f.nextSequenceNumber++ + return true } @@ -282,6 +290,7 @@ func (c *chunk) canAdd(delta uint16) bool { if len(c.deltas) < maxRunLengthCap && !c.hasDifferentTypes && delta == c.deltas[0] { return true } + return false } @@ -294,13 +303,15 @@ func (c *chunk) add(delta uint16) { func (c *chunk) encode() rtcp.PacketStatusChunk { if !c.hasDifferentTypes { defer c.reset() + return &rtcp.RunLengthChunk{ PacketStatusSymbol: c.deltas[0], - RunLength: uint16(len(c.deltas)), + RunLength: uint16(len(c.deltas)), //nolint:gosec // G115 } } if len(c.deltas) == maxOneBitCap { defer c.reset() + return &rtcp.StatusVectorChunk{ SymbolSize: rtcp.TypeTCCSymbolSizeOneBit, SymbolList: c.deltas, @@ -341,6 +352,7 @@ func maxInt(a, b int) int { if a > b { return a } + return b } @@ -348,6 +360,7 @@ func minInt(a, b int) int { if a < b { return a } + return b } @@ -355,6 +368,7 @@ func max64(a, b int64) int64 { if a > b { return a } + return b } @@ -362,5 +376,6 @@ func min64(a, b int64) int64 { if a < b { return a } + return b } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/registry.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/registry.go index e36ef6bfb..9c1201426 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/registry.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/interceptor/registry.go @@ -13,7 +13,7 @@ func (r *Registry) Add(f Factory) { r.factories = append(r.factories, f) } -// Build constructs a single Interceptor from a InterceptorRegistry +// Build constructs a single Interceptor from a InterceptorRegistry. func (r *Registry) Build(id string) (Interceptor, error) { if len(r.factories) == 0 { return &NoOp{}, nil 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 cb261304f..d622312f3 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 @@ -9,7 +9,7 @@ type RTPHeaderExtension struct { ID int } -// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded +// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded. type StreamInfo struct { ID string Attributes Attributes 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 50211be0c..59edee274 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 @@ -19,12 +19,16 @@ linters-settings: recommendations: - 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 @@ -37,6 +41,12 @@ linters-settings: - w io.Writer - r io.Reader - b []byte + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false linters: enable: @@ -59,7 +69,6 @@ linters: - 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 @@ -106,6 +115,7 @@ linters: - 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. @@ -127,9 +137,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands 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 b9824abb6..20ae88948 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 @@ -6,7 +6,7 @@

The Pion logging library

Pion transport - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status Go Reference @@ -20,9 +20,9 @@ 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) 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 eb1e56af6..b23aaa144 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 @@ -93,7 +93,7 @@ func (ll *DefaultLeveledLogger) WithOutput(output io.Writer) *DefaultLeveledLogg return ll } -func (ll *DefaultLeveledLogger) logf(logger *log.Logger, level LogLevel, format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) logf(logger *log.Logger, level LogLevel, format string, args ...any) { if ll.level.Get() < level { return } @@ -116,7 +116,7 @@ func (ll *DefaultLeveledLogger) Trace(msg string) { } // Tracef formats and emits a message if the logger is at or below LogLevelTrace. -func (ll *DefaultLeveledLogger) Tracef(format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) Tracef(format string, args ...any) { ll.logf(ll.trace, LogLevelTrace, format, args...) } @@ -126,7 +126,7 @@ func (ll *DefaultLeveledLogger) Debug(msg string) { } // Debugf formats and emits a message if the logger is at or below LogLevelDebug. -func (ll *DefaultLeveledLogger) Debugf(format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) Debugf(format string, args ...any) { ll.logf(ll.debug, LogLevelDebug, format, args...) } @@ -136,7 +136,7 @@ func (ll *DefaultLeveledLogger) Info(msg string) { } // Infof formats and emits a message if the logger is at or below LogLevelInfo. -func (ll *DefaultLeveledLogger) Infof(format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) Infof(format string, args ...any) { ll.logf(ll.info, LogLevelInfo, format, args...) } @@ -146,7 +146,7 @@ func (ll *DefaultLeveledLogger) Warn(msg string) { } // Warnf formats and emits a message if the logger is at or below LogLevelWarn. -func (ll *DefaultLeveledLogger) Warnf(format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) Warnf(format string, args ...any) { ll.logf(ll.warn, LogLevelWarn, format, args...) } @@ -156,7 +156,7 @@ func (ll *DefaultLeveledLogger) Error(msg string) { } // Errorf formats and emits a message if the logger is at or below LogLevelError. -func (ll *DefaultLeveledLogger) Errorf(format string, args ...interface{}) { +func (ll *DefaultLeveledLogger) Errorf(format string, args ...any) { ll.logf(ll.err, LogLevelError, format, args...) } 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 7b3a550ee..aac518eb5 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 @@ -58,15 +58,15 @@ const ( // LeveledLogger is the basic pion Logger interface. type LeveledLogger interface { Trace(msg string) - Tracef(format string, args ...interface{}) + Tracef(format string, args ...any) Debug(msg string) - Debugf(format string, args ...interface{}) + Debugf(format string, args ...any) Info(msg string) - Infof(format string, args ...interface{}) + Infof(format string, args ...any) Warn(msg string) - Warnf(format string, args ...interface{}) + Warnf(format string, args ...any) Error(msg string) - Errorf(format string, args ...interface{}) + Errorf(format string, args ...any) } // LoggerFactory is the basic pion LoggerFactory interface. 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 120faf29b..59edee274 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 @@ -41,6 +41,12 @@ linters-settings: - w io.Writer - r io.Reader - b []byte + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false linters: enable: 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 ff24f080f..953b3c97c 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 @@ -15,8 +15,9 @@ import ( // var ( - errH265CorruptedPacket = errors.New("corrupted h265 packet") - errInvalidH265PacketType = errors.New("invalid h265 packet type") + errH265CorruptedPacket = errors.New("corrupted h265 packet") + errInvalidH265PacketType = errors.New("invalid h265 packet type") + errExpectFragmentationStartUnit = errors.New("expecting a fragmentation start unit") ) // @@ -192,6 +193,15 @@ func (p *H265SingleNALUnitPacket) Payload() []byte { func (p *H265SingleNALUnitPacket) isH265Packet() {} +func (p *H265SingleNALUnitPacket) doPackaging(buf []byte) []byte { + buf = append(buf, annexbNALUStartCode...) + buf = append(buf, byte(p.payloadHeader>>8), byte(p.payloadHeader&0xFF)) + + buf = append(buf, p.payload...) + + return buf +} + // // Aggregation Packets implementation // @@ -399,6 +409,21 @@ func (p *H265AggregationPacket) OtherUnits() []H265AggregationUnit { func (p *H265AggregationPacket) isH265Packet() {} +func (p *H265AggregationPacket) doPackaging(buf []byte) []byte { + if p.firstUnit == nil { + return buf + } + buf = append(buf, annexbNALUStartCode...) + buf = append(buf, p.firstUnit.nalUnit...) + + for _, unit := range p.otherUnits { + buf = append(buf, annexbNALUStartCode...) + buf = append(buf, unit.nalUnit...) + } + + return buf +} + // // Fragmentation Unit implementation // @@ -536,6 +561,64 @@ func (p *H265FragmentationUnitPacket) Payload() []byte { func (p *H265FragmentationUnitPacket) isH265Packet() {} +// H265FragmentationPacket represents a Fragmentation packet, which contains one or more Fragmentation Units. +type H265FragmentationPacket struct { + payloadHeader H265NALUHeader + donl *uint16 + units []*H265FragmentationUnitPacket + payload []byte +} + +func NewH265FragmentationPacket(startUnit *H265FragmentationUnitPacket) *H265FragmentationPacket { + return &H265FragmentationPacket{ + payloadHeader: (startUnit.payloadHeader & 0x81FF) | (H265NALUHeader(startUnit.FuHeader().FuType()) << 9), + donl: startUnit.donl, + units: []*H265FragmentationUnitPacket{startUnit}, + } +} + +// PayloadHeader returns the NALU header of the packet. +func (p *H265FragmentationPacket) PayloadHeader() H265NALUHeader { + return p.payloadHeader +} + +// DONL returns the DONL of the packet. +func (p *H265FragmentationPacket) DONL() *uint16 { + return p.donl +} + +// Payload returns the Fragmentation packet payload. +func (p *H265FragmentationPacket) Payload() []byte { + return p.payload +} + +func (p *H265FragmentationPacket) isH265Packet() {} + +func (p *H265FragmentationPacket) doPackaging(buf []byte) []byte { + if len(p.payload) == 0 { + return buf + } + + buf = append(buf, annexbNALUStartCode...) + buf = append(buf, byte(p.payloadHeader>>8), byte(p.payloadHeader&0xFF)) + buf = append(buf, p.payload...) + + return buf +} + +func (p *H265FragmentationPacket) appendUnit(unit *H265FragmentationUnitPacket) { + if len(p.payload) > 0 { + // already have end unit + return + } + p.units = append(p.units, unit) + if unit.FuHeader().E() { + for _, u := range p.units { + p.payload = append(p.payload, u.payload...) + } + } +} + // // PACI implementation // @@ -691,6 +774,21 @@ func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) { func (p *H265PACIPacket) isH265Packet() {} +func (p *H265PACIPacket) doPackaging(buf []byte) []byte { + buf = append(buf, annexbNALUStartCode...) + buf = append(buf, byte(p.payloadHeader>>8), byte(p.payloadHeader&0xFF)) + + buf = binary.BigEndian.AppendUint16(buf, p.paciHeaderFields) + + if len(p.phes) > 0 { + buf = append(buf, p.phes...) + } + + buf = append(buf, p.payload...) + + return buf +} + // // Temporal Scalability Control Information // @@ -745,10 +843,11 @@ func (h H265TSCI) RES() uint8 { type isH265Packet interface { isH265Packet() + doPackaging([]byte) []byte } var ( - _ isH265Packet = (*H265FragmentationUnitPacket)(nil) + _ isH265Packet = (*H265FragmentationPacket)(nil) _ isH265Packet = (*H265PACIPacket)(nil) _ isH265Packet = (*H265SingleNALUnitPacket)(nil) _ isH265Packet = (*H265AggregationPacket)(nil) @@ -802,7 +901,15 @@ func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) { // nolint:cyclo return nil, err } - p.packet = decoded + if decoded.FuHeader().S() { + p.packet = NewH265FragmentationPacket(decoded) + } else { + if fu, ok := p.packet.(*H265FragmentationPacket); !ok { + return nil, errExpectFragmentationStartUnit + } else { + fu.appendUnit(decoded) + } + } case payloadHeader.IsAggregationPacket(): decoded := &H265AggregationPacket{} @@ -825,7 +932,7 @@ func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) { // nolint:cyclo p.packet = decoded } - return nil, nil + return p.packet.doPackaging(nil), nil } // Packet returns the populated packet. 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 b36d09af8..5e01998cf 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 @@ -10,9 +10,7 @@ import ( ) const ( - headerExtensionProfileOneByte = 0xBEDE - headerExtensionProfileTwoByte = 0x1000 - headerExtensionIDReserved = 0xF + headerExtensionIDReserved = 0xF ) // HeaderExtension represents an RTP extension header. @@ -140,7 +138,7 @@ func (e *OneByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) { profile := binary.BigEndian.Uint16(buf[0:2]) - if profile != headerExtensionProfileOneByte { + if profile != ExtensionProfileOneByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } e.payload = buf @@ -283,7 +281,7 @@ func (e *TwoByteHeaderExtension) Del(id uint8) error { // Unmarshal parses the extension payload. func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) { profile := binary.BigEndian.Uint16(buf[0:2]) - if profile != headerExtensionProfileTwoByte { + if profile != ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } e.payload = buf @@ -354,7 +352,7 @@ func (e *RawExtension) Del(id uint8) error { // Unmarshal parses the extension from the given buffer. func (e *RawExtension) Unmarshal(buf []byte) (int, error) { profile := binary.BigEndian.Uint16(buf[0:2]) - if profile == headerExtensionProfileOneByte || profile == headerExtensionProfileTwoByte { + if profile == ExtensionProfileOneByte || profile == ExtensionProfileTwoByte { return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) } e.payload = buf 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 c51d8c7d8..b0166730d 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 @@ -29,6 +29,10 @@ type Header struct { ExtensionProfile uint16 Extensions []Extension + // PaddingLength is the length of the padding in bytes. It is not part of the RTP header + // (it is sent in the last byte of RTP packet padding), but logically it belongs here. + PaddingSize byte + // Deprecated: will be removed in a future version. PayloadOffset int } @@ -36,36 +40,50 @@ type Header struct { // Packet represents an RTP Packet. type Packet struct { Header - Payload []byte - PaddingSize byte + Payload []byte + + PaddingSize byte // Deprecated: will be removed in a future version. Use Header.PaddingSize instead. // Deprecated: will be removed in a future version. Raw []byte + + // Please do not add any new field directly to Packet struct unless you know that it is safe. + // pion internally passes Header and Payload separately, what causes bugs like + // https://github.com/pion/webrtc/issues/2403 . } const ( - headerLength = 4 - versionShift = 6 - versionMask = 0x3 - paddingShift = 5 - paddingMask = 0x1 - extensionShift = 4 - extensionMask = 0x1 - extensionProfileOneByte = 0xBEDE - extensionProfileTwoByte = 0x1000 - extensionIDReserved = 0xF - ccMask = 0xF - markerShift = 7 - markerMask = 0x1 - ptMask = 0x7F - seqNumOffset = 2 - seqNumLength = 2 - timestampOffset = 4 - timestampLength = 4 - ssrcOffset = 8 - ssrcLength = 4 - csrcOffset = 12 - csrcLength = 4 + // ExtensionProfileOneByte is the RTP One Byte Header Extension Profile, defined in RFC 8285. + ExtensionProfileOneByte = 0xBEDE + // ExtensionProfileTwoByte is the RTP Two Byte Header Extension Profile, defined in RFC 8285. + ExtensionProfileTwoByte = 0x1000 + // CryptexProfileOneByte is the Cryptex One Byte Header Extension Profile, defined in RFC 9335. + CryptexProfileOneByte = 0xC0DE + // CryptexProfileTwoByte is the Cryptex Two Byte Header Extension Profile, defined in RFC 9335. + CryptexProfileTwoByte = 0xC2DE +) + +const ( + headerLength = 4 + versionShift = 6 + versionMask = 0x3 + paddingShift = 5 + paddingMask = 0x1 + extensionShift = 4 + extensionMask = 0x1 + extensionIDReserved = 0xF + ccMask = 0xF + markerShift = 7 + markerMask = 0x1 + ptMask = 0x7F + seqNumOffset = 2 + seqNumLength = 2 + timestampOffset = 4 + timestampLength = 4 + ssrcOffset = 8 + ssrcLength = 4 + csrcOffset = 12 + csrcLength = 4 ) // String helps with debugging by printing packet information in a readable way. @@ -155,7 +173,7 @@ func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit,cy return n, fmt.Errorf("size %d < %d: %w", len(buf), extensionEnd, errHeaderSizeInsufficientForExtension) } - if h.ExtensionProfile == extensionProfileOneByte || h.ExtensionProfile == extensionProfileTwoByte { + if h.ExtensionProfile == ExtensionProfileOneByte || h.ExtensionProfile == ExtensionProfileTwoByte { var ( extid uint8 payloadLen int @@ -168,7 +186,7 @@ func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit,cy continue } - if h.ExtensionProfile == extensionProfileOneByte { + if h.ExtensionProfile == ExtensionProfileOneByte { extid = buf[n] >> 4 payloadLen = int(buf[n]&^0xF0 + 1) n++ @@ -219,11 +237,12 @@ func (p *Packet) Unmarshal(buf []byte) error { if end <= n { return errTooSmall } - p.PaddingSize = buf[end-1] - end -= int(p.PaddingSize) + p.Header.PaddingSize = buf[end-1] + end -= int(p.Header.PaddingSize) } else { - p.PaddingSize = 0 + p.Header.PaddingSize = 0 } + p.PaddingSize = p.Header.PaddingSize if end < n { return errTooSmall } @@ -302,14 +321,14 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) { //nolint:cyclop switch h.ExtensionProfile { // RFC 8285 RTP One Byte Header Extension - case extensionProfileOneByte: + case ExtensionProfileOneByte: for _, extension := range h.Extensions { buf[n] = extension.id<<4 | (uint8(len(extension.payload)) - 1) // nolint: gosec // G115 n++ n += copy(buf[n:], extension.payload) } // RFC 8285 RTP Two Byte Header Extension - case extensionProfileTwoByte: + case ExtensionProfileTwoByte: for _, extension := range h.Extensions { buf[n] = extension.id n++ @@ -353,12 +372,12 @@ func (h Header) MarshalSize() int { switch h.ExtensionProfile { // RFC 8285 RTP One Byte Header Extension - case extensionProfileOneByte: + case ExtensionProfileOneByte: for _, extension := range h.Extensions { extSize += 1 + len(extension.payload) } // RFC 8285 RTP Two Byte Header Extension - case extensionProfileTwoByte: + case ExtensionProfileTwoByte: for _, extension := range h.Extensions { extSize += 2 + len(extension.payload) } @@ -378,7 +397,7 @@ func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocogni if h.Extension { // nolint: nestif switch h.ExtensionProfile { // RFC 8285 RTP One Byte Header Extension - case extensionProfileOneByte: + case ExtensionProfileOneByte: if id < 1 || id > 14 { return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id) } @@ -386,7 +405,7 @@ func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocogni return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(payload)) } // RFC 8285 RTP Two Byte Header Extension - case extensionProfileTwoByte: + case ExtensionProfileTwoByte: if id < 1 { return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id) } @@ -418,9 +437,9 @@ func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocogni switch payloadLen := len(payload); { case payloadLen <= 16: - h.ExtensionProfile = extensionProfileOneByte + h.ExtensionProfile = ExtensionProfileOneByte case payloadLen > 16 && payloadLen < 256: - h.ExtensionProfile = extensionProfileTwoByte + h.ExtensionProfile = ExtensionProfileTwoByte } h.Extensions = append(h.Extensions, Extension{id: id, payload: payload}) @@ -490,7 +509,7 @@ 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) { - if p.Header.Padding && p.PaddingSize == 0 { + if p.Header.Padding && p.paddingSize() == 0 { return 0, errInvalidRTPPadding } @@ -499,23 +518,28 @@ func (p *Packet) MarshalTo(buf []byte) (n int, err error) { return 0, err } + return marshalPayloadAndPaddingTo(buf, n, &p.Header, p.Payload, p.paddingSize()) +} + +func marshalPayloadAndPaddingTo(buf []byte, offset int, header *Header, payload []byte, paddingSize byte, +) (n int, err error) { // Make sure the buffer is large enough to hold the packet. - if n+len(p.Payload)+int(p.PaddingSize) > len(buf) { + if offset+len(payload)+int(paddingSize) > len(buf) { return 0, io.ErrShortBuffer } - m := copy(buf[n:], p.Payload) + m := copy(buf[offset:], payload) - if p.Header.Padding { - buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize + if header.Padding { + buf[offset+m+int(paddingSize-1)] = paddingSize } - return n + m + int(p.PaddingSize), nil + return offset + m + int(paddingSize), nil } // MarshalSize returns the size of the packet once marshaled. func (p Packet) MarshalSize() int { - return p.Header.MarshalSize() + len(p.Payload) + int(p.PaddingSize) + return p.Header.MarshalSize() + len(p.Payload) + int(p.paddingSize()) } // Clone returns a deep copy of p. @@ -552,3 +576,45 @@ func (h Header) Clone() Header { return clone } + +func (p *Packet) paddingSize() byte { + if p.Header.PaddingSize > 0 { + return p.Header.PaddingSize + } + + return p.PaddingSize +} + +// MarshalPacketTo serializes the header and payload into bytes. +// Parts of pion code passes RTP header and payload separately, so this function +// is provided to help with that. +// +// Deprecated: this function is a temporary workaround and will be removed in pion/webrtc v5. +func MarshalPacketTo(buf []byte, header *Header, payload []byte) (int, error) { + n, err := header.MarshalTo(buf) + if err != nil { + return 0, err + } + + return marshalPayloadAndPaddingTo(buf, n, header, payload, header.PaddingSize) +} + +// PacketMarshalSize returns the size of the header and payload once marshaled. +// Parts of pion code passes RTP header and payload separately, so this function +// is provided to help with that. +// +// Deprecated: this function is a temporary workaround and will be removed in pion/webrtc v5. +func PacketMarshalSize(header *Header, payload []byte) int { + return header.MarshalSize() + len(payload) + int(header.PaddingSize) +} + +// HeaderAndPacketMarshalSize returns the size of the header and full packet once marshaled. +// Parts of pion code passes RTP header and payload separately, so this function +// is provided to help with that. +// +// Deprecated: this function is a temporary workaround and will be removed in pion/webrtc v5. +func HeaderAndPacketMarshalSize(header *Header, payload []byte) (headerSize int, packetSize int) { + headerSize = header.MarshalSize() + + return headerSize, headerSize + len(payload) + int(header.PaddingSize) +} 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 c0dbea49f..181d0d2f1 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 @@ -165,9 +165,6 @@ func (p *packetizer) GeneratePadding(samples uint32) []*Packet { 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, @@ -179,8 +176,8 @@ func (p *packetizer) GeneratePadding(samples uint32) []*Packet { Timestamp: p.Timestamp, // Use latest timestamp SSRC: p.SSRC, CSRC: []uint32{}, + PaddingSize: 255, }, - Payload: pp, } } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml index 88cb4fbf9..120faf29b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml @@ -19,12 +19,16 @@ linters-settings: recommendations: - 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 @@ -127,9 +131,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md index 1cb8dbd21..32fa30dd7 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md @@ -7,10 +7,10 @@

Pion SDP Sourcegraph Widget - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status - Go Reference + Go Reference Coverage Status Go Report Card License: MIT @@ -21,9 +21,9 @@ 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) @@ -32,4 +32,4 @@ If you need commercial support or don't want to use public methods you can conta 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 \ No newline at end of file +MIT License - see [LICENSE](LICENSE) for full text diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go index 388a13e9e..ce0ba53ab 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go @@ -23,6 +23,7 @@ const ( AttrKeyConnectionSetup = "setup" AttrKeyMID = "mid" AttrKeyICELite = "ice-lite" + AttrKeyICEOptions = "ice-options" AttrKeyRTCPMux = "rtcp-mux" AttrKeyRTCPRsize = "rtcp-rsize" AttrKeyInactive = "inactive" @@ -31,6 +32,7 @@ const ( AttrKeySendRecv = "sendrecv" AttrKeyExtMap = "extmap" AttrKeyExtMapAllowMixed = "extmap-allow-mixed" + AttrKeyCryptex = "cryptex" ) // Constants for semantic tokens used in JSEP. @@ -38,7 +40,9 @@ const ( SemanticTokenLipSynchronization = "LS" SemanticTokenFlowIdentification = "FID" SemanticTokenForwardErrorCorrection = "FEC" - SemanticTokenWebRTCMediaStreams = "WMS" + // https://datatracker.ietf.org/doc/html/rfc5956#section-4.1 + SemanticTokenForwardErrorCorrectionFramework = "FEC-FR" + SemanticTokenWebRTCMediaStreams = "WMS" ) // Constants for extmap key. @@ -113,6 +117,12 @@ func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescr return s } +// WithICETrickleAdvertised advertises ICE trickle support in the session description. +// See https://datatracker.ietf.org/doc/html/rfc9429#section-5.2.1 +func (s *SessionDescription) WithICETrickleAdvertised() *SessionDescription { + return s.WithValueAttribute(AttrKeyICEOptions, "trickle") +} + // WithFingerprint adds a fingerprint to the session description. func (s *SessionDescription) WithFingerprint(algorithm, value string) *SessionDescription { return s.WithValueAttribute("fingerprint", algorithm+" "+value) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go index e884b0f9f..8ef01e15c 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go @@ -986,10 +986,10 @@ func timeShorthand(b byte) int64 { func parsePort(value string) (int, error) { port, err := strconv.Atoi(value) if err != nil { - return 0, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, port) + return 0, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, value) } - if port < 0 || port > 65536 { + if port < 0 || port > 65535 { return 0, fmt.Errorf("%w -- out of range `%v`", errSDPInvalidPortValue, port) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go index 4550feece..7cf17a961 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go @@ -19,16 +19,12 @@ const ( ) var ( - errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap") - errExtractCodecFmtp = errors.New("could not extract codec from fmtp") - errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb") - errMultipleName = errors.New("codec has multiple names defined") - errMultipleClockRate = errors.New("codec has multiple clock rates") - errMultipleEncodingParameters = errors.New("codec has multiple encoding parameters") - errMultipleFmtp = errors.New("codec has multiple fmtp values") - errPayloadTypeNotFound = errors.New("payload type not found") - errCodecNotFound = errors.New("codec not found") - errSyntaxError = errors.New("SyntaxError") + errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap") + errExtractCodecFmtp = errors.New("could not extract codec from fmtp") + errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb") + errPayloadTypeNotFound = errors.New("payload type not found") + errCodecNotFound = errors.New("codec not found") + errSyntaxError = errors.New("SyntaxError") ) // ConnectionRole indicates which of the end points should initiate the connection establishment. @@ -207,49 +203,30 @@ func parseRtcpFb(rtcpFb string) (codec Codec, isWildcard bool, err error) { return codec, isWildcard, nil } -func mergeCodecs(codec Codec, codecs map[uint8]Codec) error { // nolint: cyclop +func mergeCodecs(codec Codec, codecs map[uint8]Codec) { savedCodec := codecs[codec.PayloadType] - savedCodec.PayloadType = codec.PayloadType - - if codec.Name != "" { - if savedCodec.Name != "" && savedCodec.Name != codec.Name { - return errMultipleName - } + if savedCodec.PayloadType == 0 { + savedCodec.PayloadType = codec.PayloadType + } + if savedCodec.Name == "" { savedCodec.Name = codec.Name } - - if codec.ClockRate != 0 { - if savedCodec.ClockRate != 0 && savedCodec.ClockRate != codec.ClockRate { - return errMultipleClockRate - } - + if savedCodec.ClockRate == 0 { savedCodec.ClockRate = codec.ClockRate } - - if codec.EncodingParameters != "" { - if savedCodec.EncodingParameters != "" && savedCodec.EncodingParameters != codec.EncodingParameters { - return errMultipleEncodingParameters - } - + if savedCodec.EncodingParameters == "" { savedCodec.EncodingParameters = codec.EncodingParameters } - - if codec.Fmtp != "" { - if savedCodec.Fmtp != "" && savedCodec.Fmtp != codec.Fmtp { - return errMultipleFmtp - } - + if savedCodec.Fmtp == "" { savedCodec.Fmtp = codec.Fmtp } - savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...) - codecs[savedCodec.PayloadType] = savedCodec - return nil + codecs[savedCodec.PayloadType] = savedCodec } -func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint:cyclop, gocognit +func (s *SessionDescription) buildCodecMap() map[uint8]Codec { //nolint:cyclop codecs := map[uint8]Codec{ // static codecs that do not require a rtpmap 0: { @@ -272,16 +249,12 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint case strings.HasPrefix(attr, "rtpmap:"): codec, err := parseRtpmap(attr) if err == nil { - if err = mergeCodecs(codec, codecs); err != nil { - return nil, err - } + mergeCodecs(codec, codecs) } case strings.HasPrefix(attr, "fmtp:"): codec, err := parseFmtp(attr) if err == nil { - if err = mergeCodecs(codec, codecs); err != nil { - return nil, err - } + mergeCodecs(codec, codecs) } case strings.HasPrefix(attr, "rtcp-fb:"): codec, isWildcard, err := parseRtcpFb(attr) @@ -290,9 +263,7 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint case isWildcard: wildcardRTCPFeedback = append(wildcardRTCPFeedback, codec.RTCPFeedback...) default: - if err = mergeCodecs(codec, codecs); err != nil { - return nil, err - } + mergeCodecs(codec, codecs) } } } @@ -306,7 +277,7 @@ func (s *SessionDescription) buildCodecMap() (map[uint8]Codec, error) { //nolint codecs[i] = codec } - return codecs, nil + return codecs } func equivalentFmtp(want, got string) bool { @@ -350,10 +321,7 @@ func codecsMatch(wanted, got Codec) bool { // GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec. func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) { - codecs, err := s.buildCodecMap() - if err != nil { - return Codec{}, err - } + codecs := s.buildCodecMap() codec, ok := codecs[payloadType] if ok { @@ -366,10 +334,7 @@ func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, e // GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec // as closely as possible and returns its payload type. func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) { - codecs, err := s.buildCodecMap() - if err != nil { - return 0, err - } + codecs := s.buildCodecMap() for payloadType, codec := range codecs { if codecsMatch(wanted, codec) { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/.golangci.yml index a3235bec2..120faf29b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/.golangci.yml +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/.golangci.yml @@ -19,23 +19,42 @@ linters-settings: recommendations: - 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 + - 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`. @@ -46,18 +65,17 @@ linters: - 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 - - 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 - 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 @@ -65,9 +83,15 @@ linters: - 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 @@ -75,28 +99,22 @@ linters: - 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 + - 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 - - containedctx # containedctx is a linter that detects struct contained context.Context field - - cyclop # checks function and package cyclomatic complexity - 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. + - 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 - - lll # Reports long lines - - maintidx # maintidx measures the maintainability index of each function. - - makezero # Finds slice declarations with non-zero initial length - - 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 + - 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 @@ -104,8 +122,7 @@ linters: - 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 + - 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! @@ -114,9 +131,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/README.md index d6fdbd0d3..10ede23cd 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/README.md +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/README.md @@ -7,7 +7,7 @@

Pion SRTP Sourcegraph Widget - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status Go Reference @@ -21,9 +21,9 @@ 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) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/context.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/context.go index cb2ed56a7..3bced572a 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/context.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/context.go @@ -24,11 +24,9 @@ const ( seqNumMedian = 1 << 15 seqNumMax = 1 << 16 - - srtcpIndexSize = 4 ) -// Encrypt/Decrypt state for a single SRTP SSRC +// Encrypt/Decrypt state for a single SRTP SSRC. type srtpSSRCState struct { ssrc uint32 rolloverHasProcessed bool @@ -36,13 +34,30 @@ type srtpSSRCState struct { replayDetector replaydetector.ReplayDetector } -// Encrypt/Decrypt state for a single SRTCP SSRC +// Encrypt/Decrypt state for a single SRTCP SSRC. type srtcpSSRCState struct { srtcpIndex uint32 ssrc uint32 replayDetector replaydetector.ReplayDetector } +// RCCMode is the mode of Roll-over Counter Carrying Transform from RFC 4771. +type RCCMode int + +const ( + // RCCModeNone is the default mode. + RCCModeNone RCCMode = iota + // RCCMode1 is RCCm1 mode from RFC 4771. In this mode ROC and truncated auth tag is sent every R-th packet, + // and no auth tag in other ones. This mode is not supported by pion/srtp. + RCCMode1 + // RCCMode2 is RCCm2 mode from RFC 4771. In this mode ROC and truncated auth tag is sent every R-th packet, + // and full auth tag in other ones. This mode is supported for AES-CM and NULL profiles only. + RCCMode2 + // RCCMode3 is RCCm3 mode from RFC 4771. In this mode ROC is sent every R-th packet (without truncated auth tag), + // and no auth tag in other ones. This mode is supported for AES-GCM profiles only. + RCCMode3 +) + // Context represents a SRTP cryptographic context. // Context can only be used for one-way operations. // it must either used ONLY for encryption or ONLY for decryption. @@ -60,11 +75,18 @@ type Context struct { profile ProtectionProfile - sendMKI []byte // Master Key Identifier used for encrypting RTP/RTCP packets. Set to nil if MKI is not enabled. - mkis map[string]srtpCipher // Master Key Identifier to cipher mapping. Used for decrypting packets. Empty if MKI is not enabled. + // Master Key Identifier used for encrypting RTP/RTCP packets. Set to nil if MKI is not enabled. + sendMKI []byte + // Master Key Identifier to cipher mapping. Used for decrypting packets. Empty if MKI is not enabled. + mkis map[string]srtpCipher encryptSRTP bool encryptSRTCP bool + + rccMode RCCMode + rocTransmitRate uint16 + + authTagRTPLen *int } // CreateContext creates a new SRTP Context. @@ -74,7 +96,11 @@ type Context struct { // Following example create SRTP Context with replay protection with window size of 256. // // decCtx, err := srtp.CreateContext(key, salt, profile, srtp.SRTPReplayProtection(256)) -func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts ...ContextOption) (c *Context, err error) { +func CreateContext( + masterKey, masterSalt []byte, + profile ProtectionProfile, + opts ...ContextOption, +) (c *Context, err error) { c = &Context{ srtpSSRCStates: map[uint32]*srtpSSRCState{}, srtcpSSRCStates: map[uint32]*srtcpSSRCState{}, @@ -96,6 +122,21 @@ func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts } } + if err = c.checkRCCMode(); err != nil { + return nil, err + } + + if c.authTagRTPLen != nil { + var authKeyLen int + authKeyLen, err = c.profile.AuthKeyLen() + if err != nil { + return nil, err + } + if *c.authTagRTPLen > authKeyLen { + return nil, errTooLongSRTPAuthTag + } + } + c.cipher, err = c.createCipher(c.sendMKI, masterKey, masterSalt, c.encryptSRTP, c.encryptSRTCP) if err != nil { return nil, err @@ -107,7 +148,8 @@ func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts return c, nil } -// AddCipherForMKI adds new MKI with associated masker key and salt. Context must be created with MasterKeyIndicator option +// AddCipherForMKI adds new MKI with associated masker key and salt. +// Context must be created with MasterKeyIndicator option // to enable MKI support. MKI must be unique and have the same length as the one used for creating Context. // Operation is not thread-safe, you need to provide synchronization with decrypting packets. func (c *Context) AddCipherForMKI(mki, masterKey, masterSalt []byte) error { @@ -126,6 +168,7 @@ func (c *Context) AddCipherForMKI(mki, masterKey, masterSalt []byte) error { return err } c.mkis[string(mki)] = cipher + return nil } @@ -141,18 +184,26 @@ func (c *Context) createCipher(mki, masterKey, masterSalt []byte, encryptSRTP, e } if masterKeyLen := len(masterKey); masterKeyLen != keyLen { - return nil, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterKey, masterKey, keyLen) + return nil, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterKey, keyLen, masterKey) } else if masterSaltLen := len(masterSalt); masterSaltLen != saltLen { return nil, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterSalt, saltLen, masterSaltLen) } + profileWithArgs := protectionProfileWithArgs{ + ProtectionProfile: c.profile, + authTagRTPLen: c.authTagRTPLen, + } + switch c.profile { case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: - return newSrtpCipherAeadAesGcm(c.profile, masterKey, masterSalt, mki, encryptSRTP, encryptSRTCP) - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80: - return newSrtpCipherAesCmHmacSha1(c.profile, masterKey, masterSalt, mki, encryptSRTP, encryptSRTCP) + return newSrtpCipherAeadAesGcm(profileWithArgs, masterKey, masterSalt, mki, encryptSRTP, encryptSRTCP) + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_80: + return newSrtpCipherAesCmHmacSha1(profileWithArgs, masterKey, masterSalt, mki, encryptSRTP, encryptSRTCP) case ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: - return newSrtpCipherAesCmHmacSha1(c.profile, masterKey, masterSalt, mki, false, false) + return newSrtpCipherAesCmHmacSha1(profileWithArgs, masterKey, masterSalt, mki, false, false) default: return nil, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, c.profile) } @@ -168,6 +219,7 @@ func (c *Context) RemoveMKI(mki []byte) error { return errMKIAlreadyInUse } delete(c.mkis, string(mki)) + return nil } @@ -180,19 +232,20 @@ func (c *Context) SetSendMKI(mki []byte) error { } c.sendMKI = mki c.cipher = cipher + return nil } // https://tools.ietf.org/html/rfc3550#appendix-A.1 -func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (roc uint32, diff int32, overflow bool) { +func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (roc uint32, diff int64, overflow bool) { seq := int32(sequenceNumber) - localRoc := uint32(s.index >> 16) - localSeq := int32(s.index & (seqNumMax - 1)) + localRoc := uint32(s.index >> 16) //nolint:gosec // G115 + localSeq := int32(s.index & (seqNumMax - 1)) //nolint:gosec // G115 guessRoc := localRoc var difference int32 - if s.rolloverHasProcessed { + if s.rolloverHasProcessed { //nolint:nestif // When localROC is equal to 0, and entering seq-localSeq > seqNumMedian // judgment, it will cause guessRoc calculation error if s.index > seqNumMedian { @@ -219,16 +272,20 @@ func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (roc uint32, di } } - return guessRoc, difference, (guessRoc == 0 && localRoc == maxROC) + return guessRoc, int64(difference), (guessRoc == 0 && localRoc == maxROC) } -func (s *srtpSSRCState) updateRolloverCount(sequenceNumber uint16, difference int32) { - if !s.rolloverHasProcessed { +func (s *srtpSSRCState) updateRolloverCount(sequenceNumber uint16, difference int64, hasRemoteRoc bool, + remoteRoc uint32, +) { + switch { + case hasRemoteRoc: + s.index = (uint64(remoteRoc) << 16) | uint64(sequenceNumber) + s.rolloverHasProcessed = true + case !s.rolloverHasProcessed: s.index |= uint64(sequenceNumber) s.rolloverHasProcessed = true - return - } - if difference > 0 { + case difference > 0: s.index += uint64(difference) } } @@ -244,6 +301,7 @@ func (c *Context) getSRTPSSRCState(ssrc uint32) *srtpSSRCState { replayDetector: c.newSRTPReplayDetector(), } c.srtpSSRCStates[ssrc] = s + return s } @@ -258,6 +316,7 @@ func (c *Context) getSRTCPSSRCState(ssrc uint32) *srtcpSSRCState { replayDetector: c.newSRTCPReplayDetector(), } c.srtcpSSRCStates[ssrc] = s + return s } @@ -267,7 +326,8 @@ func (c *Context) ROC(ssrc uint32) (uint32, bool) { if !ok { return 0, false } - return uint32(s.index >> 16), true + + return uint32(s.index >> 16), true //nolint:gosec // G115 } // SetROC sets SRTP rollover counter value of specified SSRC. @@ -283,6 +343,7 @@ func (c *Context) Index(ssrc uint32) (uint32, bool) { if !ok { return 0, false } + return s.srtcpIndex, true } @@ -291,3 +352,49 @@ func (c *Context) SetIndex(ssrc uint32, index uint32) { s := c.getSRTCPSSRCState(ssrc) s.srtcpIndex = index % (maxSRTCPIndex + 1) } + +//nolint:cyclop +func (c *Context) checkRCCMode() error { + if c.rccMode == RCCModeNone { + return nil + } + + if c.rocTransmitRate == 0 { + return errZeroRocTransmitRate + } + + switch c.profile { + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: + // AEAD profiles support RCCMode3 only + if c.rccMode != RCCMode3 { + return errUnsupportedRccMode + } + + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileNullHmacSha1_32: + if c.authTagRTPLen == nil { + // ROC completely replaces auth tag for _32 profiles. If you really want to use 4-byte + // SRTP auth tag with RCC, use SRTPAuthenticationTagLength(4) option. + return errTooShortSRTPAuthTag + } + + fallthrough // Checks below are common for _32 and _80 profiles. + + case ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_80, + ProtectionProfileNullHmacSha1_80: + // AES-CM and NULL profiles support RCCMode2 only + if c.rccMode != RCCMode2 { + return errUnsupportedRccMode + } + if c.authTagRTPLen != nil && *c.authTagRTPLen < 4 { + return errTooShortSRTPAuthTag + } + + default: + return errUnsupportedRccMode + } + + return nil +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/crypto.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/crypto.go index 3f1913017..4c82f81b6 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/crypto.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/crypto.go @@ -55,5 +55,6 @@ func xorBytesCTR(block cipher.Block, iv []byte, dst, src []byte) error { } i += n } + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/errors.go index c22653f0b..e46da068b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/errors.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/errors.go @@ -9,9 +9,9 @@ import ( ) var ( - // ErrFailedToVerifyAuthTag is returned when decryption fails due to invalid authentication tag + // ErrFailedToVerifyAuthTag is returned when decryption fails due to invalid authentication tag. ErrFailedToVerifyAuthTag = errors.New("failed to verify auth tag") - // ErrMKINotFound is returned when decryption fails due to unknown MKI value in packet + // ErrMKINotFound is returned when decryption fails due to unknown MKI value in packet. ErrMKINotFound = errors.New("MKI not found") errDuplicated = errors.New("duplicated packet") @@ -31,11 +31,16 @@ var ( errMKIAlreadyInUse = errors.New("MKI already in use") errMKIIsNotEnabled = errors.New("MKI is not enabled") errInvalidMKILength = errors.New("invalid MKI length") + errTooLongSRTPAuthTag = errors.New("SRTP auth tag is too long") + errTooShortSRTPAuthTag = errors.New("SRTP auth tag is too short") errStreamNotInited = errors.New("stream has not been inited, unable to close") errStreamAlreadyClosed = errors.New("stream is already closed") errStreamAlreadyInited = errors.New("stream is already inited") errFailedTypeAssertion = errors.New("failed to cast child") + + errZeroRocTransmitRate = errors.New("ROC transmit rate is zero") + errUnsupportedRccMode = errors.New("unsupported RCC mode") ) type duplicatedError struct { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/key_derivation.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/key_derivation.go index f192fafcc..945b569b8 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/key_derivation.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/key_derivation.go @@ -40,6 +40,7 @@ func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr i block.Encrypt(out[n:n+nBlockSize], prfIn) i++ } + return out[:outLen], nil } @@ -50,8 +51,12 @@ func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr i // - times the 16-bit RTP sequence number has been reset to zero after // - passing through 65,535 // i = 2^16 * ROC + SEQ -// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16) -func generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) (counter [16]byte) { +// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16). +func generateCounter( + sequenceNumber uint16, + rolloverCounter uint32, + ssrc uint32, sessionSalt []byte, +) (counter [16]byte) { copy(counter[:], sessionSalt) counter[4] ^= byte(ssrc >> 24) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/keying.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/keying.go index 617f4d71c..c9dc183c2 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/keying.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/keying.go @@ -5,7 +5,7 @@ package srtp const labelExtractorDtlsSrtp = "EXTRACTOR-dtls_srtp" -// KeyingMaterialExporter allows package SRTP to extract keying material +// KeyingMaterialExporter allows package SRTP to extract keying material. type KeyingMaterialExporter interface { ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) } @@ -46,6 +46,7 @@ func (c *Config) ExtractSessionKeysFromDTLS(exporter KeyingMaterialExporter, isC c.Keys.LocalMasterSalt = clientWriteKey[keyLen:] c.Keys.RemoteMasterKey = serverWriteKey[0:keyLen] c.Keys.RemoteMasterSalt = serverWriteKey[keyLen:] + return nil } @@ -53,5 +54,6 @@ func (c *Config) ExtractSessionKeysFromDTLS(exporter KeyingMaterialExporter, isC c.Keys.LocalMasterSalt = serverWriteKey[keyLen:] c.Keys.RemoteMasterKey = clientWriteKey[0:keyLen] c.Keys.RemoteMasterSalt = clientWriteKey[keyLen:] + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/option.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/option.go index dac0bcf1d..e9c6f8744 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/option.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/option.go @@ -16,6 +16,7 @@ func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:revive c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { return replaydetector.New(windowSize, maxROC<<16|maxSequenceNumber) } + return nil } } @@ -26,6 +27,7 @@ func SRTCPReplayProtection(windowSize uint) ContextOption { c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector { return replaydetector.New(windowSize, maxSRTCPIndex) } + return nil } } @@ -36,6 +38,7 @@ func SRTPNoReplayProtection() ContextOption { // nolint:revive c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { return &nopReplayDetector{} } + return nil } } @@ -46,6 +49,7 @@ func SRTCPNoReplayProtection() ContextOption { c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector { return &nopReplayDetector{} } + return nil } } @@ -54,6 +58,7 @@ func SRTCPNoReplayProtection() ContextOption { func SRTPReplayDetectorFactory(fn func() replaydetector.ReplayDetector) ContextOption { // nolint:revive return func(c *Context) error { c.newSRTPReplayDetector = fn + return nil } } @@ -62,6 +67,7 @@ func SRTPReplayDetectorFactory(fn func() replaydetector.ReplayDetector) ContextO func SRTCPReplayDetectorFactory(fn func() replaydetector.ReplayDetector) ContextOption { return func(c *Context) error { c.newSRTCPReplayDetector = fn + return nil } } @@ -81,6 +87,7 @@ func MasterKeyIndicator(mki []byte) ContextOption { c.sendMKI = make([]byte, len(mki)) copy(c.sendMKI, mki) } + return nil } } @@ -89,15 +96,20 @@ func MasterKeyIndicator(mki []byte) ContextOption { func SRTPEncryption() ContextOption { // nolint:revive return func(c *Context) error { c.encryptSRTP = true + return nil } } -// SRTPNoEncryption disables SRTP encryption. This option is useful when you want to use NullCipher for SRTP and keep authentication only. +// SRTPNoEncryption disables SRTP encryption. +// This option is useful when you want to use NullCipher for SRTP and keep authentication only. // It simplifies debugging and testing, but it is not recommended for production use. +// +// Note: you can also use SRTPAuthenticationTagLength(0) to disable authentication tag too. func SRTPNoEncryption() ContextOption { // nolint:revive return func(c *Context) error { c.encryptSRTP = false + return nil } } @@ -106,15 +118,58 @@ func SRTPNoEncryption() ContextOption { // nolint:revive func SRTCPEncryption() ContextOption { return func(c *Context) error { c.encryptSRTCP = true + return nil } } -// SRTCPNoEncryption disables SRTCP encryption. This option is useful when you want to use NullCipher for SRTCP and keep authentication only. +// SRTCPNoEncryption disables SRTCP encryption. +// This option is useful when you want to use NullCipher for SRTCP and keep authentication only. // It simplifies debugging and testing, but it is not recommended for production use. func SRTCPNoEncryption() ContextOption { return func(c *Context) error { c.encryptSRTCP = false + + return nil + } +} + +// RolloverCounterCarryingTransform enables Rollover Counter Carrying Transform from RFC 4771. +// ROC value is sent in Authentication Tag of SRTP packets every rocTransmitRate packets. +// +// RFC 4771 defines 3 RCC modes. pion/srtp supports mode RCCm2 for AES-CM and NULL profiles, +// and mode RCCm3 for AES-GCM (AEAD) profiles. +// +// From RFC 4771: "[For modes RCCm1 and and RCCm3] the length of the MAC is shorter than the length +// of the authentication tag. To achieve the same (or less) MAC forgery success probability on all +// packets when using RCCm1 or RCCm2, as with the default integrity transform in RFC 3711, +// the tag-length must be set to 14 octets, which means that the length of MAC_tr is 10 octets." +// +// Protection profiles ProtectionProfile*CmHmacSha1_32 uses 4-byte SRTP auth tag, so in RCCm2 mode +// SRTP packets with ROC will not be integrity protected. +// +// You can increase the length of the authentication tag using SRTPAuthenticationTagLength option +// to mitigate this issue. +func RolloverCounterCarryingTransform(mode RCCMode, rocTransmitRate uint16) ContextOption { + return func(c *Context) error { + c.rccMode = mode + c.rocTransmitRate = rocTransmitRate + + return nil + } +} + +// SRTPAuthenticationTagLength sets length of SRTP authentication tag in bytes for AES-CM protection +// profiles. Decreasing the length of the authentication tag is not recommended for production use, +// as it decreases integrity protection. +// +// Zero value means that there is no authentication tag, what may be useful for debugging and testing. +// +// This option is ignored for AEAD profiles. +func SRTPAuthenticationTagLength(authTagRTPLen int) ContextOption { // nolint:revive + return func(c *Context) error { + c.authTagRTPLen = &authTagRTPLen + return nil } } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile.go index 9384bf8f9..181da221b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile.go @@ -5,19 +5,25 @@ package srtp import "fmt" -// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite +// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite. type ProtectionProfile uint16 // Supported protection profiles // See https://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml // -// AES128_CM_HMAC_SHA1_80 and AES128_CM_HMAC_SHA1_32 are valid SRTP profiles, but they do not have an DTLS-SRTP Protection Profiles ID assigned -// in RFC 5764. They were in earlier draft of this RFC: https://datatracker.ietf.org/doc/html/draft-ietf-avt-dtls-srtp-03#section-4.1.2 +// AES128_CM_HMAC_SHA1_80 and AES128_CM_HMAC_SHA1_32 are valid SRTP profiles, +// but they do not have an DTLS-SRTP Protection Profiles ID assigned +// in RFC 5764. They were in earlier draft of this RFC: +// https://datatracker.ietf.org/doc/html/draft-ietf-avt-dtls-srtp-03#section-4.1.2 // Their IDs are now marked as reserved in the IANA registry. Despite this Chrome supports them: // https://chromium.googlesource.com/chromium/deps/libsrtp/+/84122798bb16927b1e676bd4f938a6e48e5bf2fe/srtp/include/srtp.h#694 // -// Null profiles disable encryption, they are used for debugging and testing. They are not recommended for production use. -// Use of them is equivalent to using ProtectionProfileAes128CmHmacSha1_NN profile with SRTPNoEncryption and SRTCPNoEncryption options. +// Null profiles disable encryption, they are used for debugging and testing. +// They are not recommended for production use. +// Use of them is equivalent to using ProtectionProfileAes128CmHmacSha1_NN +// profile with SRTPNoEncryption and SRTCPNoEncryption options. +// +//nolint:lll const ( ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001 ProtectionProfileAes128CmHmacSha1_32 ProtectionProfile = 0x0002 @@ -29,10 +35,16 @@ const ( ProtectionProfileAeadAes256Gcm ProtectionProfile = 0x0008 ) -// KeyLen returns length of encryption key in bytes. For all profiles except NullHmacSha1_32 and NullHmacSha1_80 is is also the length of the session key. +// KeyLen returns length of encryption key in bytes. +// For all profiles except NullHmacSha1_32 and NullHmacSha1_80 is +// also the length of the session key. func (p ProtectionProfile) KeyLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAeadAes128Gcm, ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAeadAes128Gcm, + ProtectionProfileNullHmacSha1_32, + ProtectionProfileNullHmacSha1_80: return 16, nil case ProtectionProfileAeadAes256Gcm, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80: return 32, nil @@ -41,10 +53,17 @@ func (p ProtectionProfile) KeyLen() (int, error) { } } -// SaltLen returns length of salt key in bytes. For all profiles except NullHmacSha1_32 and NullHmacSha1_80 is is also the length of the session salt. +// SaltLen returns length of salt key in bytes. +// For all profiles except NullHmacSha1_32 and NullHmacSha1_80 +// is also the length of the session salt. func (p ProtectionProfile) SaltLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80, ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_80, + ProtectionProfileNullHmacSha1_32, + ProtectionProfileNullHmacSha1_80: return 14, nil case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 12, nil @@ -53,7 +72,8 @@ func (p ProtectionProfile) SaltLen() (int, error) { } } -// AuthTagRTPLen returns length of RTP authentication tag in bytes for AES protection profiles. For AEAD ones it returns zero. +// AuthTagRTPLen returns length of RTP authentication tag in bytes for AES protection profiles. +// For AEAD ones it returns zero. func (p ProtectionProfile) AuthTagRTPLen() (int, error) { switch p { case ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_80, ProtectionProfileNullHmacSha1_80: @@ -67,10 +87,17 @@ func (p ProtectionProfile) AuthTagRTPLen() (int, error) { } } -// AuthTagRTCPLen returns length of RTCP authentication tag in bytes for AES protection profiles. For AEAD ones it returns zero. +// AuthTagRTCPLen returns length of RTCP authentication tag in bytes for AES protection profiles. +// +// For AEAD ones it returns zero. func (p ProtectionProfile) AuthTagRTCPLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80, ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_80, + ProtectionProfileNullHmacSha1_32, + ProtectionProfileNullHmacSha1_80: return 10, nil case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 0, nil @@ -79,10 +106,16 @@ func (p ProtectionProfile) AuthTagRTCPLen() (int, error) { } } -// AEADAuthTagLen returns length of authentication tag in bytes for AEAD protection profiles. For AES ones it returns zero. +// AEADAuthTagLen returns length of authentication tag in bytes for AEAD protection profiles. +// For AES ones it returns zero. func (p ProtectionProfile) AEADAuthTagLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80, ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_80, + ProtectionProfileNullHmacSha1_32, + ProtectionProfileNullHmacSha1_80: return 0, nil case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 16, nil @@ -91,10 +124,16 @@ func (p ProtectionProfile) AEADAuthTagLen() (int, error) { } } -// AuthKeyLen returns length of authentication key in bytes for AES protection profiles. For AEAD ones it returns zero. +// AuthKeyLen returns length of authentication key in bytes for AES protection profiles. +// For AEAD ones it returns zero. func (p ProtectionProfile) AuthKeyLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAes256CmHmacSha1_32, ProtectionProfileAes256CmHmacSha1_80, ProtectionProfileNullHmacSha1_32, ProtectionProfileNullHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, + ProtectionProfileAes128CmHmacSha1_80, + ProtectionProfileAes256CmHmacSha1_32, + ProtectionProfileAes256CmHmacSha1_80, + ProtectionProfileNullHmacSha1_32, + ProtectionProfileNullHmacSha1_80: return 20, nil case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 0, nil diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile_with_args.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile_with_args.go new file mode 100644 index 000000000..a0e08be4e --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/protection_profile_with_args.go @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package srtp + +// protectionProfileWithArgs is a wrapper around ProtectionProfile that allows to +// specify additional arguments for the profile. +type protectionProfileWithArgs struct { + ProtectionProfile + authTagRTPLen *int +} + +// AuthTagRTPLen returns length of RTP authentication tag in bytes for AES protection profiles. +// For AEAD ones it returns zero. +func (p protectionProfileWithArgs) AuthTagRTPLen() (int, error) { + if p.authTagRTPLen != nil { + return *p.authTagRTPLen, nil + } + + return p.ProtectionProfile.AuthTagRTPLen() +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session.go index cc4f60095..ea4e59894 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session.go @@ -58,7 +58,7 @@ type Config struct { LocalOptions, RemoteOptions []ContextOption } -// SessionKeys bundles the keys required to setup an SRTP session +// SessionKeys bundles the keys required to setup an SRTP session. type SessionKeys struct { LocalMasterKey []byte LocalMasterSalt []byte @@ -74,20 +74,21 @@ func (s *session) getOrCreateReadStream(ssrc uint32, child streamSession, proto return nil, false } - r, ok := s.readStreams[ssrc] + rStream, ok := s.readStreams[ssrc] if ok { - return r, false + return rStream, false } // Create the readStream. - r = proto() + rStream = proto() - if err := r.init(child, ssrc); err != nil { + if err := rStream.init(child, ssrc); err != nil { return nil, false } - s.readStreams[ssrc] = r - return r, true + s.readStreams[ssrc] = rStream + + return rStream, true } func (s *session) removeReadStream(ssrc uint32) { @@ -109,10 +110,15 @@ func (s *session) close() error { } <-s.closed + return nil } -func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remoteMasterSalt []byte, profile ProtectionProfile, child streamSession) error { +func (s *session) start( + localMasterKey, localMasterSalt, remoteMasterKey, remoteMasterSalt []byte, + profile ProtectionProfile, + child streamSession, +) error { var err error s.localContext, err = CreateContext(localMasterKey, localMasterSalt, profile, s.localOptions...) if err != nil { @@ -146,6 +152,7 @@ func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remote if !errors.Is(err, io.EOF) { s.log.Error(err.Error()) } + return } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtcp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtcp.go index 13f1a9589..8d6f30669 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtcp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtcp.go @@ -16,7 +16,7 @@ const defaultSessionSRTCPReplayProtectionWindow = 64 // SessionSRTCP implements io.ReadWriteCloser and provides a bi-directional SRTCP session // SRTCP itself does not have a design like this, but it is common in most applications // for local/remote to each have their own keying material. This provides those patterns -// instead of making everyone re-implement +// instead of making everyone re-implement. type SessionSRTCP struct { session writeStream *WriteStreamSRTCP @@ -47,7 +47,7 @@ func NewSessionSRTCP(conn net.Conn, config *Config) (*SessionSRTCP, error) { //n config.RemoteOptions..., ) - s := &SessionSRTCP{ + srtcpSession := &SessionSRTCP{ session: session{ nextConn: conn, localOptions: localOpts, @@ -61,37 +61,39 @@ func NewSessionSRTCP(conn net.Conn, config *Config) (*SessionSRTCP, error) { //n log: loggerFactory.NewLogger("srtp"), }, } - s.writeStream = &WriteStreamSRTCP{s} + srtcpSession.writeStream = &WriteStreamSRTCP{srtcpSession} - err := s.session.start( + err := srtcpSession.session.start( config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt, config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt, config.Profile, - s, + srtcpSession, ) if err != nil { return nil, err } - return s, nil + + return srtcpSession, nil } -// OpenWriteStream returns the global write stream for the Session +// OpenWriteStream returns the global write stream for the Session. func (s *SessionSRTCP) OpenWriteStream() (*WriteStreamSRTCP, error) { return s.writeStream, nil } // OpenReadStream opens a read stream for the given SSRC, it can be used -// if you want a certain SSRC, but don't want to wait for AcceptStream +// if you want a certain SSRC, but don't want to wait for AcceptStream. func (s *SessionSRTCP) OpenReadStream(ssrc uint32) (*ReadStreamSRTCP, error) { r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTCP) if readStream, ok := r.(*ReadStreamSRTCP); ok { return readStream, nil } + return nil, errFailedTypeAssertion } -// AcceptStream returns a stream to handle RTCP for a single SSRC +// AcceptStream returns a stream to handle RTCP for a single SSRC. func (s *SessionSRTCP) AcceptStream() (*ReadStreamSRTCP, uint32, error) { stream, ok := <-s.newStream if !ok { @@ -106,7 +108,7 @@ func (s *SessionSRTCP) AcceptStream() (*ReadStreamSRTCP, uint32, error) { return readStream, stream.GetSSRC(), nil } -// Close ends the session +// Close ends the session. func (s *SessionSRTCP) Close() error { return s.session.close() } @@ -122,12 +124,13 @@ func (s *SessionSRTCP) write(buf []byte) (int, error) { defer bufferpool.Put(ibuf) s.session.localContextMutex.Lock() - encrypted, err := s.localContext.EncryptRTCP(ibuf.([]byte), buf, nil) + encrypted, err := s.localContext.EncryptRTCP(ibuf.([]byte), buf, nil) //nolint:forcetypeassert s.session.localContextMutex.Unlock() if err != nil { return 0, err } + return s.session.nextConn.Write(encrypted) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtp.go index e07cbe216..5512255f1 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/session_srtp.go @@ -17,7 +17,7 @@ const defaultSessionSRTPReplayProtectionWindow = 64 // SessionSRTP implements io.ReadWriteCloser and provides a bi-directional SRTP session // SRTP itself does not have a design like this, but it is common in most applications // for local/remote to each have their own keying material. This provides those patterns -// instead of making everyone re-implement +// instead of making everyone re-implement. type SessionSRTP struct { session writeStream *WriteStreamSRTP @@ -48,7 +48,7 @@ func NewSessionSRTP(conn net.Conn, config *Config) (*SessionSRTP, error) { //nol config.RemoteOptions..., ) - s := &SessionSRTP{ + srtpSession := &SessionSRTP{ session: session{ nextConn: conn, localOptions: localOpts, @@ -62,27 +62,28 @@ func NewSessionSRTP(conn net.Conn, config *Config) (*SessionSRTP, error) { //nol log: loggerFactory.NewLogger("srtp"), }, } - s.writeStream = &WriteStreamSRTP{s} + srtpSession.writeStream = &WriteStreamSRTP{srtpSession} - err := s.session.start( + err := srtpSession.session.start( config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt, config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt, config.Profile, - s, + srtpSession, ) if err != nil { return nil, err } - return s, nil + + return srtpSession, nil } -// OpenWriteStream returns the global write stream for the Session +// OpenWriteStream returns the global write stream for the Session. func (s *SessionSRTP) OpenWriteStream() (*WriteStreamSRTP, error) { return s.writeStream, nil } // OpenReadStream opens a read stream for the given SSRC, it can be used -// if you want a certain SSRC, but don't want to wait for AcceptStream +// if you want a certain SSRC, but don't want to wait for AcceptStream. func (s *SessionSRTP) OpenReadStream(ssrc uint32) (*ReadStreamSRTP, error) { r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTP) @@ -93,7 +94,7 @@ func (s *SessionSRTP) OpenReadStream(ssrc uint32) (*ReadStreamSRTP, error) { return nil, errFailedTypeAssertion } -// AcceptStream returns a stream to handle RTCP for a single SSRC +// AcceptStream returns a stream to handle RTCP for a single SSRC. func (s *SessionSRTP) AcceptStream() (*ReadStreamSRTP, uint32, error) { stream, ok := <-s.newStream if !ok { @@ -108,7 +109,7 @@ func (s *SessionSRTP) AcceptStream() (*ReadStreamSRTP, uint32, error) { return readStream, stream.GetSSRC(), nil } -// Close ends the session +// Close ends the session. func (s *SessionSRTP) Close() error { return s.session.close() } @@ -149,8 +150,20 @@ func (s *SessionSRTP) writeRTP(header *rtp.Header, payload []byte) (int, error) ibuf := bufferpool.Get() defer bufferpool.Put(ibuf) + buf := ibuf.([]byte) // nolint:forcetypeassert + headerLen, marshalSize := rtp.HeaderAndPacketMarshalSize(header, payload) // nolint:staticcheck + if len(buf) < marshalSize+20 { + // The buffer is too small, so we need to allocate a new one. Add 20 bytes for auth tag like + // for bufferpool above. + buf = make([]byte, marshalSize+20) + } + _, err := rtp.MarshalPacketTo(buf, header, payload) // nolint:staticcheck + if err != nil { + return 0, err + } + s.session.localContextMutex.Lock() - encrypted, err := s.localContext.encryptRTP(ibuf.([]byte), header, payload) + encrypted, err := s.localContext.encryptRTP(buf, header, headerLen, buf[:marshalSize]) s.session.localContextMutex.Unlock() if err != nil { @@ -165,13 +178,13 @@ func (s *SessionSRTP) setWriteDeadline(t time.Time) error { } func (s *SessionSRTP) decrypt(buf []byte) error { - h := &rtp.Header{} - headerLen, err := h.Unmarshal(buf) + header := &rtp.Header{} + headerLen, err := header.Unmarshal(buf) if err != nil { return err } - r, isNew := s.session.getOrCreateReadStream(h.SSRC, s, newReadStreamSRTP) + r, isNew := s.session.getOrCreateReadStream(header.SSRC, s, newReadStreamSRTP) if r == nil { return nil // Session has been closed } else if isNew { @@ -186,7 +199,7 @@ func (s *SessionSRTP) decrypt(buf []byte) error { return errFailedTypeAssertion } - decrypted, err := s.remoteContext.decryptRTP(buf, buf, h, headerLen) + decrypted, err := s.remoteContext.decryptRTP(buf, buf, header, headerLen) if err != nil { return err } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtcp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtcp.go index 6d1a1c1d9..6f7050806 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtcp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtcp.go @@ -10,9 +10,23 @@ import ( "github.com/pion/rtcp" ) -const maxSRTCPIndex = 0x7FFFFFFF +/* +Simplified structure of SRTCP Packets: +- RTCP Header +- Payload +- AEAD Auth Tag - used by AEAD profiles only +- E flag and SRTCP Index +- MKI (optional) +- Auth Tag - used by non-AEAD profiles only +*/ -const srtcpHeaderSize = 8 +const ( + maxSRTCPIndex = 0x7FFFFFFF + + srtcpHeaderSize = 8 + srtcpIndexSize = 4 + srtcpEncryptionFlag = 0x80 +) func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) { authTagLen, err := c.cipher.AuthTagRTCPLen() @@ -42,25 +56,24 @@ func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) { cipher := c.cipher if len(c.mkis) > 0 { // Find cipher for MKI - actualMKI := c.cipher.getMKI(encrypted, false) + actualMKI := encrypted[len(encrypted)-mkiLen-authTagLen : len(encrypted)-authTagLen] cipher, ok = c.mkis[string(actualMKI)] if !ok { return nil, ErrMKINotFound } } - out := allocateIfMismatch(dst, encrypted) - - out, err = cipher.decryptRTCP(out, encrypted, index, ssrc) + out, err := cipher.decryptRTCP(dst, encrypted, index, ssrc) if err != nil { return nil, err } markAsValid() + return out, nil } -// DecryptRTCP decrypts a buffer that contains a RTCP packet +// DecryptRTCP decrypts a buffer that contains a RTCP packet. func (c *Context) DecryptRTCP(dst, encrypted []byte, header *rtcp.Header) ([]byte, error) { if header == nil { header = &rtcp.Header{} @@ -79,9 +92,9 @@ func (c *Context) encryptRTCP(dst, decrypted []byte) ([]byte, error) { } ssrc := binary.BigEndian.Uint32(decrypted[4:]) - s := c.getSRTCPSSRCState(ssrc) + ssrcState := c.getSRTCPSSRCState(ssrc) - if s.srtcpIndex >= maxSRTCPIndex { + if ssrcState.srtcpIndex >= maxSRTCPIndex { // ... when 2^48 SRTP packets or 2^31 SRTCP packets have been secured with the same key // (whichever occurs before), the key management MUST be called to provide new master key(s) // (previously stored and used keys MUST NOT be used again), or the session MUST be terminated. @@ -90,12 +103,12 @@ func (c *Context) encryptRTCP(dst, decrypted []byte) ([]byte, error) { } // We roll over early because MSB is used for marking as encrypted - s.srtcpIndex++ + ssrcState.srtcpIndex++ - return c.cipher.encryptRTCP(dst, decrypted, s.srtcpIndex, ssrc) + return c.cipher.encryptRTCP(dst, decrypted, ssrcState.srtcpIndex, ssrc) } -// EncryptRTCP Encrypts a RTCP packet +// EncryptRTCP Encrypts a RTCP packet. func (c *Context) EncryptRTCP(dst, decrypted []byte, header *rtcp.Header) ([]byte, error) { if header == nil { header = &rtcp.Header{} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp.go index 56828bcf4..89e427f13 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp.go @@ -5,11 +5,21 @@ package srtp import ( + "encoding/binary" "fmt" "github.com/pion/rtp" ) +/* +Simplified structure of SRTP Packets: +- RTP Header (with optional RTP Header Extension) +- Payload (with optional padding) +- AEAD Auth Tag - used by AEAD profiles only +- MKI (optional) +- Auth Tag - used by non-AEAD profiles only. When RCC is used with AEAD profiles, the ROC is sent here. +*/ + func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int) ([]byte, error) { authTagLen, err := c.cipher.AuthTagRTPLen() if err != nil { @@ -21,17 +31,31 @@ func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerL } mkiLen := len(c.sendMKI) + var hasRocInPacket bool + hasRocInPacket, authTagLen = c.hasROCInPacket(header, authTagLen) + // Verify that encrypted packet is long enough if len(ciphertext) < (headerLen + aeadAuthTagLen + mkiLen + authTagLen) { return nil, fmt.Errorf("%w: %d", errTooShortRTP, len(ciphertext)) } - s := c.getSRTPSSRCState(header.SSRC) + ssrcState := c.getSRTPSSRCState(header.SSRC) - roc, diff, _ := s.nextRolloverCount(header.SequenceNumber) - markAsValid, ok := s.replayDetector.Check( - (uint64(roc) << 16) | uint64(header.SequenceNumber), - ) + var roc uint32 + var diff int64 + var index uint64 + if !hasRocInPacket { + // The ROC is not sent in the packet. We need to guess it. + roc, diff, _ = ssrcState.nextRolloverCount(header.SequenceNumber) + index = (uint64(roc) << 16) | uint64(header.SequenceNumber) + } else { + // Extract ROC from the packet. The ROC is sent in the first 4 bytes of the auth tag. + roc = binary.BigEndian.Uint32(ciphertext[len(ciphertext)-authTagLen:]) + index = (uint64(roc) << 16) | uint64(header.SequenceNumber) + diff = int64(ssrcState.index) - int64(index) //nolint:gosec + } + + markAsValid, ok := ssrcState.replayDetector.Check(index) if !ok { return nil, &duplicatedError{ Proto: "srtp", SSRC: header.SSRC, Index: uint32(header.SequenceNumber), @@ -41,7 +65,7 @@ func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerL cipher := c.cipher if len(c.mkis) > 0 { // Find cipher for MKI - actualMKI := c.cipher.getMKI(ciphertext, true) + actualMKI := ciphertext[len(ciphertext)-mkiLen-authTagLen : len(ciphertext)-authTagLen] cipher, ok = c.mkis[string(actualMKI)] if !ok { return nil, ErrMKINotFound @@ -50,17 +74,18 @@ func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerL dst = growBufferSize(dst, len(ciphertext)-authTagLen-len(c.sendMKI)) - dst, err = cipher.decryptRTP(dst, ciphertext, header, headerLen, roc) + dst, err = cipher.decryptRTP(dst, ciphertext, header, headerLen, roc, hasRocInPacket) if err != nil { return nil, err } markAsValid() - s.updateRolloverCount(header.SequenceNumber, diff) + ssrcState.updateRolloverCount(header.SequenceNumber, diff, hasRocInPacket, roc) + return dst, nil } -// DecryptRTP decrypts a RTP packet with an encrypted payload +// DecryptRTP decrypts a RTP packet with an encrypted payload. func (c *Context) DecryptRTP(dst, encrypted []byte, header *rtp.Header) ([]byte, error) { if header == nil { header = &rtp.Header{} @@ -75,7 +100,8 @@ func (c *Context) DecryptRTP(dst, encrypted []byte, header *rtp.Header) ([]byte, } // EncryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. -// If the dst buffer does not have the capacity to hold `len(plaintext) + 10` bytes, a new one will be allocated and returned. +// If the dst buffer does not have the capacity to hold `len(plaintext) + 10` bytes, +// a new one will be allocated and returned. // If a rtp.Header is provided, it will be Unmarshaled using the plaintext. func (c *Context) EncryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ([]byte, error) { if header == nil { @@ -87,13 +113,14 @@ func (c *Context) EncryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ( return nil, err } - return c.encryptRTP(dst, header, plaintext[headerLen:]) + return c.encryptRTP(dst, header, headerLen, plaintext) } // encryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. // If the dst buffer does not have the capacity, a new one will be allocated and returned. // Similar to above but faster because it can avoid unmarshaling the header and marshaling the payload. -func (c *Context) encryptRTP(dst []byte, header *rtp.Header, payload []byte) (ciphertext []byte, err error) { +func (c *Context) encryptRTP(dst []byte, header *rtp.Header, headerLen int, plaintext []byte, +) (ciphertext []byte, err error) { s := c.getSRTPSSRCState(header.SSRC) roc, diff, ovf := s.nextRolloverCount(header.SequenceNumber) if ovf { @@ -103,7 +130,30 @@ func (c *Context) encryptRTP(dst []byte, header *rtp.Header, payload []byte) (ci // https://www.rfc-editor.org/rfc/rfc3711#section-9.2 return nil, errExceededMaxPackets } - s.updateRolloverCount(header.SequenceNumber, diff) + s.updateRolloverCount(header.SequenceNumber, diff, false, 0) - return c.cipher.encryptRTP(dst, header, payload, roc) + rocInPacket := false + if c.rccMode != RCCModeNone && header.SequenceNumber%c.rocTransmitRate == 0 { + rocInPacket = true + } + + return c.cipher.encryptRTP(dst, header, headerLen, plaintext, roc, rocInPacket) +} + +func (c *Context) hasROCInPacket(header *rtp.Header, authTagLen int) (bool, int) { + hasRocInPacket := false + switch c.rccMode { + case RCCMode2: + // This mode is supported for AES-CM and NULL profiles only. The ROC is sent in the first 4 bytes of the auth tag. + hasRocInPacket = header.SequenceNumber%c.rocTransmitRate == 0 + case RCCMode3: + // This mode is supported for AES-GCM only. The ROC is sent as 4-byte auth tag. + hasRocInPacket = header.SequenceNumber%c.rocTransmitRate == 0 + if hasRocInPacket { + authTagLen = 4 + } + default: + } + + return hasRocInPacket, authTagLen } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher.go index da745e7cd..3464e3e62 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher.go @@ -6,7 +6,7 @@ package srtp import "github.com/pion/rtp" // cipher represents a implementation of one -// of the SRTP Specific ciphers +// of the SRTP Specific ciphers. type srtpCipher interface { // AuthTagRTPLen/AuthTagRTCPLen return auth key length of the cipher. // See the note below. @@ -16,12 +16,11 @@ type srtpCipher interface { // See the note below. AEADAuthTagLen() (int, error) getRTCPIndex([]byte) uint32 - getMKI([]byte, bool) []byte - encryptRTP([]byte, *rtp.Header, []byte, uint32) ([]byte, error) + encryptRTP([]byte, *rtp.Header, int, []byte, uint32, bool) ([]byte, error) encryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error) - decryptRTP([]byte, []byte, *rtp.Header, int, uint32) ([]byte, error) + decryptRTP([]byte, []byte, *rtp.Header, int, uint32, bool) ([]byte, error) decryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aead_aes_gcm.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aead_aes_gcm.go index 64f890fa9..3dbb1424c 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aead_aes_gcm.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aead_aes_gcm.go @@ -12,12 +12,8 @@ import ( "github.com/pion/rtp" ) -const ( - rtcpEncryptionFlag = 0x80 -) - type srtpCipherAeadAesGcm struct { - ProtectionProfile + protectionProfileWithArgs srtpCipher, srtcpCipher cipher.AEAD @@ -28,11 +24,15 @@ type srtpCipherAeadAesGcm struct { srtpEncrypted, srtcpEncrypted bool } -func newSrtpCipherAeadAesGcm(profile ProtectionProfile, masterKey, masterSalt, mki []byte, encryptSRTP, encryptSRTCP bool) (*srtpCipherAeadAesGcm, error) { - s := &srtpCipherAeadAesGcm{ - ProtectionProfile: profile, - srtpEncrypted: encryptSRTP, - srtcpEncrypted: encryptSRTCP, +func newSrtpCipherAeadAesGcm( + profile protectionProfileWithArgs, + masterKey, masterSalt, mki []byte, + encryptSRTP, encryptSRTCP bool, +) (*srtpCipherAeadAesGcm, error) { + srtpCipher := &srtpCipherAeadAesGcm{ + protectionProfileWithArgs: profile, + srtpEncrypted: encryptSRTP, + srtcpEncrypted: encryptSRTCP, } srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) @@ -45,7 +45,7 @@ func newSrtpCipherAeadAesGcm(profile ProtectionProfile, masterKey, masterSalt, m return nil, err } - s.srtpCipher, err = cipher.NewGCM(srtpBlock) + srtpCipher.srtpCipher, err = cipher.NewGCM(srtpBlock) if err != nil { return nil, err } @@ -60,72 +60,109 @@ func newSrtpCipherAeadAesGcm(profile ProtectionProfile, masterKey, masterSalt, m return nil, err } - s.srtcpCipher, err = cipher.NewGCM(srtcpBlock) + srtpCipher.srtcpCipher, err = cipher.NewGCM(srtcpBlock) if err != nil { return nil, err } - if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + if srtpCipher.srtpSessionSalt, err = aesCmKeyDerivation( + labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt), + ); err != nil { return nil, err - } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + } else if srtpCipher.srtcpSessionSalt, err = aesCmKeyDerivation( + labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt), + ); err != nil { return nil, err } mkiLen := len(mki) if mkiLen > 0 { - s.mki = make([]byte, mkiLen) - copy(s.mki, mki) + srtpCipher.mki = make([]byte, mkiLen) + copy(srtpCipher.mki, mki) } - return s, nil + return srtpCipher, nil } -func (s *srtpCipherAeadAesGcm) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { +func (s *srtpCipherAeadAesGcm) encryptRTP( + dst []byte, + header *rtp.Header, + headerLen int, + plaintext []byte, + roc uint32, + rocInAuthTag bool, +) (ciphertext []byte, err error) { + payload := plaintext[headerLen:] + payloadLen := len(payload) + // Grow the given buffer to fit the output. authTagLen, err := s.AEADAuthTagLen() if err != nil { return nil, err } - dst = growBufferSize(dst, header.MarshalSize()+len(payload)+authTagLen+len(s.mki)) + authPartLen := header.MarshalSize() + len(payload) + authTagLen + dstLen := authPartLen + len(s.mki) + if rocInAuthTag { + dstLen += 4 + } + dst = growBufferSize(dst, dstLen) + sameBuffer := isSameBuffer(dst, plaintext) - n, err := header.MarshalTo(dst) - if err != nil { - return nil, err + // Copy the header unencrypted. + if !sameBuffer { + copy(dst, plaintext[:headerLen]) } iv := s.rtpInitializationVector(header, roc) if s.srtpEncrypted { - s.srtpCipher.Seal(dst[n:n], iv[:], payload, dst[:n]) + s.srtpCipher.Seal(dst[headerLen:headerLen], iv[:], payload, dst[:headerLen]) } else { - clearLen := n + len(payload) - copy(dst[n:], payload) + clearLen := headerLen + payloadLen + if !sameBuffer { + copy(dst[headerLen:], payload) + } s.srtpCipher.Seal(dst[clearLen:clearLen], iv[:], nil, dst[:clearLen]) } // Add MKI after the encrypted payload if len(s.mki) > 0 { - copy(dst[len(dst)-len(s.mki):], s.mki) + copy(dst[authPartLen:], s.mki) + } + + if rocInAuthTag { + binary.BigEndian.PutUint32(dst[len(dst)-4:], roc) } return dst, nil } -func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int, roc uint32) ([]byte, error) { +func (s *srtpCipherAeadAesGcm) decryptRTP( + dst, ciphertext []byte, + header *rtp.Header, + headerLen int, + roc uint32, + rocInAuthTag bool, +) ([]byte, error) { // Grow the given buffer to fit the output. authTagLen, err := s.AEADAuthTagLen() if err != nil { return nil, err } - nDst := len(ciphertext) - authTagLen - len(s.mki) + rocLen := 0 + if rocInAuthTag { + rocLen = 4 + } + nDst := len(ciphertext) - authTagLen - len(s.mki) - rocLen if nDst < headerLen { // Size of ciphertext is shorter than AEAD auth tag len. return nil, ErrFailedToVerifyAuthTag } dst = growBufferSize(dst, nDst) + sameBuffer := isSameBuffer(dst, ciphertext) iv := s.rtpInitializationVector(header, roc) - nEnd := len(ciphertext) - len(s.mki) + nEnd := len(ciphertext) - len(s.mki) - rocLen if s.srtpEncrypted { if _, err := s.srtpCipher.Open( dst[headerLen:headerLen], iv[:], ciphertext[headerLen:nEnd], ciphertext[:headerLen], @@ -139,10 +176,16 @@ func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.He ); err != nil { return nil, fmt.Errorf("%w: %w", ErrFailedToVerifyAuthTag, err) } - copy(dst[headerLen:], ciphertext[headerLen:nDataEnd]) + if !sameBuffer { + copy(dst[headerLen:], ciphertext[headerLen:nDataEnd]) + } + } + + // Copy the header unencrypted. + if !sameBuffer { + copy(dst[:headerLen], ciphertext[:headerLen]) } - copy(dst[:headerLen], ciphertext[:headerLen]) return dst, nil } @@ -154,28 +197,36 @@ func (s *srtpCipherAeadAesGcm) encryptRTCP(dst, decrypted []byte, srtcpIndex uin aadPos := len(decrypted) + authTagLen // Grow the given buffer to fit the output. dst = growBufferSize(dst, aadPos+srtcpIndexSize+len(s.mki)) + sameBuffer := isSameBuffer(dst, decrypted) iv := s.rtcpInitializationVector(srtcpIndex, ssrc) if s.srtcpEncrypted { aad := s.rtcpAdditionalAuthenticatedData(decrypted, srtcpIndex) - copy(dst[:8], decrypted[:8]) - copy(dst[aadPos:aadPos+4], aad[8:12]) - s.srtcpCipher.Seal(dst[8:8], iv[:], decrypted[8:], aad[:]) + if !sameBuffer { + // Copy the header unencrypted. + copy(dst[:srtcpHeaderSize], decrypted[:srtcpHeaderSize]) + } + // Copy index to the proper place. + copy(dst[aadPos:aadPos+srtcpIndexSize], aad[8:12]) + s.srtcpCipher.Seal(dst[srtcpHeaderSize:srtcpHeaderSize], iv[:], decrypted[srtcpHeaderSize:], aad[:]) } else { // Copy the packet unencrypted. - copy(dst, decrypted) + if !sameBuffer { + copy(dst, decrypted) + } // Append the SRTCP index to the end of the packet - this will form the AAD. binary.BigEndian.PutUint32(dst[len(decrypted):], srtcpIndex) // Generate the authentication tag. tag := make([]byte, authTagLen) - s.srtcpCipher.Seal(tag[0:0], iv[:], nil, dst[:len(decrypted)+4]) + s.srtcpCipher.Seal(tag[0:0], iv[:], nil, dst[:len(decrypted)+srtcpIndexSize]) // Copy index to the proper place. - copy(dst[aadPos:], dst[len(decrypted):len(decrypted)+4]) + copy(dst[aadPos:], dst[len(decrypted):len(decrypted)+srtcpIndexSize]) // Copy the auth tag after RTCP payload. copy(dst[len(decrypted):], tag) } - copy(dst[aadPos+4:], s.mki) + copy(dst[aadPos+srtcpIndexSize:], s.mki) + return dst, nil } @@ -192,12 +243,14 @@ func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ss return nil, ErrFailedToVerifyAuthTag } dst = growBufferSize(dst, nDst) + sameBuffer := isSameBuffer(dst, encrypted) - isEncrypted := encrypted[aadPos]>>7 != 0 + isEncrypted := encrypted[aadPos]&srtcpEncryptionFlag != 0 iv := s.rtcpInitializationVector(srtcpIndex, ssrc) if isEncrypted { aad := s.rtcpAdditionalAuthenticatedData(encrypted, srtcpIndex) - if _, err := s.srtcpCipher.Open(dst[8:8], iv[:], encrypted[8:aadPos], aad[:]); err != nil { + if _, err := s.srtcpCipher.Open(dst[srtcpHeaderSize:srtcpHeaderSize], iv[:], encrypted[srtcpHeaderSize:aadPos], + aad[:]); err != nil { return nil, fmt.Errorf("%w: %w", ErrFailedToVerifyAuthTag, err) } } else { @@ -211,10 +264,16 @@ func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ss return nil, fmt.Errorf("%w: %w", ErrFailedToVerifyAuthTag, err) } // Copy the unencrypted payload. - copy(dst[8:], encrypted[8:dataEnd]) + if !sameBuffer { + copy(dst[srtcpHeaderSize:], encrypted[srtcpHeaderSize:dataEnd]) + } + } + + // Copy the header unencrypted. + if !sameBuffer { + copy(dst[:srtcpHeaderSize], encrypted[:srtcpHeaderSize]) } - copy(dst[:8], encrypted[:8]) return dst, nil } @@ -233,6 +292,7 @@ func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc u for i := range iv { iv[i] ^= s.srtpSessionSalt[i] } + return iv } @@ -252,6 +312,7 @@ func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc for i := range iv { iv[i] ^= s.srtcpSessionSalt[i] } + return iv } @@ -265,21 +326,11 @@ func (s *srtpCipherAeadAesGcm) rtcpAdditionalAuthenticatedData(rtcpPacket []byte copy(aad[:], rtcpPacket[:8]) binary.BigEndian.PutUint32(aad[8:], srtcpIndex) - aad[8] |= rtcpEncryptionFlag + aad[8] |= srtcpEncryptionFlag return aad } func (s *srtpCipherAeadAesGcm) getRTCPIndex(in []byte) uint32 { - return binary.BigEndian.Uint32(in[len(in)-len(s.mki)-4:]) &^ (rtcpEncryptionFlag << 24) -} - -func (s *srtpCipherAeadAesGcm) getMKI(in []byte, _ bool) []byte { - mkiLen := len(s.mki) - if mkiLen == 0 { - return nil - } - - tailOffset := len(in) - mkiLen - return in[tailOffset:] + return binary.BigEndian.Uint32(in[len(in)-len(s.mki)-srtcpIndexSize:]) &^ (srtcpEncryptionFlag << 24) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aes_cm_hmac_sha1.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aes_cm_hmac_sha1.go index aa673fa94..307b6d3bb 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aes_cm_hmac_sha1.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/srtp_cipher_aes_cm_hmac_sha1.go @@ -16,7 +16,7 @@ import ( //nolint:gci ) type srtpCipherAesCmHmacSha1 struct { - ProtectionProfile + protectionProfileWithArgs srtpSessionSalt []byte srtpSessionAuth hash.Hash @@ -31,35 +31,46 @@ type srtpCipherAesCmHmacSha1 struct { mki []byte } -func newSrtpCipherAesCmHmacSha1(profile ProtectionProfile, masterKey, masterSalt, mki []byte, encryptSRTP, encryptSRTCP bool) (*srtpCipherAesCmHmacSha1, error) { - if profile == ProtectionProfileNullHmacSha1_80 || profile == ProtectionProfileNullHmacSha1_32 { +//nolint:cyclop +func newSrtpCipherAesCmHmacSha1( + profile protectionProfileWithArgs, + masterKey, masterSalt, mki []byte, + encryptSRTP, encryptSRTCP bool, +) (*srtpCipherAesCmHmacSha1, error) { + switch profile.ProtectionProfile { + case ProtectionProfileNullHmacSha1_80, ProtectionProfileNullHmacSha1_32: encryptSRTP = false encryptSRTCP = false + default: } - s := &srtpCipherAesCmHmacSha1{ - ProtectionProfile: profile, - srtpEncrypted: encryptSRTP, - srtcpEncrypted: encryptSRTCP, + srtpCipher := &srtpCipherAesCmHmacSha1{ + protectionProfileWithArgs: profile, + srtpEncrypted: encryptSRTP, + srtcpEncrypted: encryptSRTCP, } srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) if err != nil { return nil, err - } else if s.srtpBlock, err = aes.NewCipher(srtpSessionKey); err != nil { + } else if srtpCipher.srtpBlock, err = aes.NewCipher(srtpSessionKey); err != nil { return nil, err } srtcpSessionKey, err := aesCmKeyDerivation(labelSRTCPEncryption, masterKey, masterSalt, 0, len(masterKey)) if err != nil { return nil, err - } else if s.srtcpBlock, err = aes.NewCipher(srtcpSessionKey); err != nil { + } else if srtpCipher.srtcpBlock, err = aes.NewCipher(srtcpSessionKey); err != nil { return nil, err } - if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + if srtpCipher.srtpSessionSalt, err = aesCmKeyDerivation( + labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt), + ); err != nil { return nil, err - } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil { + } else if srtpCipher.srtcpSessionSalt, err = aesCmKeyDerivation( + labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt), + ); err != nil { return nil, err } @@ -78,45 +89,55 @@ func newSrtpCipherAesCmHmacSha1(profile ProtectionProfile, masterKey, masterSalt return nil, err } - s.srtcpSessionAuth = hmac.New(sha1.New, srtcpSessionAuthTag) - s.srtpSessionAuth = hmac.New(sha1.New, srtpSessionAuthTag) + srtpCipher.srtcpSessionAuth = hmac.New(sha1.New, srtcpSessionAuthTag) + srtpCipher.srtpSessionAuth = hmac.New(sha1.New, srtpSessionAuthTag) mkiLen := len(mki) if mkiLen > 0 { - s.mki = make([]byte, mkiLen) - copy(s.mki, mki) + srtpCipher.mki = make([]byte, mkiLen) + copy(srtpCipher.mki, mki) } - return s, nil + return srtpCipher, nil } -func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { +func (s *srtpCipherAesCmHmacSha1) encryptRTP( + dst []byte, + header *rtp.Header, + headerLen int, + plaintext []byte, + roc uint32, + rocInAuthTag bool, +) (ciphertext []byte, err error) { + payload := plaintext[headerLen:] + payloadLen := len(payload) + // Grow the given buffer to fit the output. authTagLen, err := s.AuthTagRTPLen() if err != nil { return nil, err } - dst = growBufferSize(dst, header.MarshalSize()+len(payload)+len(s.mki)+authTagLen) + dst = growBufferSize(dst, headerLen+payloadLen+len(s.mki)+authTagLen) + sameBuffer := isSameBuffer(dst, plaintext) // Copy the header unencrypted. - n, err := header.MarshalTo(dst) - if err != nil { - return nil, err + if !sameBuffer { + copy(dst, plaintext[:headerLen]) } // Encrypt the payload if s.srtpEncrypted { counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt) - if err = xorBytesCTR(s.srtpBlock, counter[:], dst[n:], payload); err != nil { + if err = xorBytesCTR(s.srtpBlock, counter[:], dst[headerLen:], payload); err != nil { return nil, err } - } else { - copy(dst[n:], payload) + } else if !sameBuffer { + copy(dst[headerLen:], payload) } - n += len(payload) + n := headerLen + payloadLen // Generate the auth tag. - authTag, err := s.generateSrtpAuthTag(dst[:n], roc) + authTag, err := s.generateSrtpAuthTag(dst[:n], roc, rocInAuthTag) if err != nil { return nil, err } @@ -133,7 +154,13 @@ func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, pay return dst, nil } -func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int, roc uint32) ([]byte, error) { +func (s *srtpCipherAesCmHmacSha1) decryptRTP( + dst, ciphertext []byte, + header *rtp.Header, + headerLen int, + roc uint32, + rocInAuthTag bool, +) ([]byte, error) { // Split the auth tag and the cipher text into two parts. authTagLen, err := s.AuthTagRTPLen() if err != nil { @@ -145,7 +172,7 @@ func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp ciphertext = ciphertext[:len(ciphertext)-len(s.mki)-authTagLen] // Generate the auth tag we expect to see from the ciphertext. - expectedTag, err := s.generateSrtpAuthTag(ciphertext, roc) + expectedTag, err := s.generateSrtpAuthTag(ciphertext, roc, rocInAuthTag) if err != nil { return nil, err } @@ -156,8 +183,12 @@ func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp return nil, ErrFailedToVerifyAuthTag } + sameBuffer := isSameBuffer(dst, ciphertext) + // Write the plaintext header to the destination buffer. - copy(dst, ciphertext[:headerLen]) + if !sameBuffer { + copy(dst, ciphertext[:headerLen]) + } // Decrypt the ciphertext for the payload. if s.srtpEncrypted { @@ -168,83 +199,110 @@ func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp if err != nil { return nil, err } - } else { + } else if !sameBuffer { copy(dst[headerLen:], ciphertext[headerLen:]) } + return dst, nil } func (s *srtpCipherAesCmHmacSha1) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) { - dst = allocateIfMismatch(dst, decrypted) + authTagLen, err := s.AuthTagRTCPLen() + if err != nil { + return nil, err + } + mkiLen := len(s.mki) + decryptedLen := len(decrypted) + encryptedLen := decryptedLen + authTagLen + mkiLen + srtcpIndexSize + + dst = growBufferSize(dst, encryptedLen) + sameBuffer := isSameBuffer(dst, decrypted) + + if !sameBuffer { + copy(dst, decrypted[:srtcpHeaderSize]) // Copy the first 8 bytes (RTCP header) + } // Encrypt everything after header if s.srtcpEncrypted { - counter := generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt) - if err := xorBytesCTR(s.srtcpBlock, counter[:], dst[8:], dst[8:]); err != nil { + counter := generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt) //nolint:gosec // G115 + if err = xorBytesCTR(s.srtcpBlock, counter[:], dst[srtcpHeaderSize:], decrypted[srtcpHeaderSize:]); err != nil { return nil, err } // Add SRTCP Index and set Encryption bit - dst = append(dst, make([]byte, 4)...) - binary.BigEndian.PutUint32(dst[len(dst)-4:], srtcpIndex) - dst[len(dst)-4] |= 0x80 + binary.BigEndian.PutUint32(dst[decryptedLen:], srtcpIndex) + dst[decryptedLen] |= srtcpEncryptionFlag } else { // Copy the decrypted payload as is - copy(dst[8:], decrypted[8:]) + if !sameBuffer { + copy(dst[srtcpHeaderSize:], decrypted[srtcpHeaderSize:]) + } // Add SRTCP Index with Encryption bit cleared - dst = append(dst, make([]byte, 4)...) - binary.BigEndian.PutUint32(dst[len(dst)-4:], srtcpIndex) + binary.BigEndian.PutUint32(dst[decryptedLen:], srtcpIndex) } + n := decryptedLen + srtcpIndexSize + // Generate the authentication tag - authTag, err := s.generateSrtcpAuthTag(dst) + authTag, err := s.generateSrtcpAuthTag(dst[:n]) if err != nil { return nil, err } // Include the MKI if provided if len(s.mki) > 0 { - dst = append(dst, s.mki...) + copy(dst[n:], s.mki) + n += mkiLen } // Append the auth tag at the end of the buffer - return append(dst, authTag...), nil + copy(dst[n:], authTag) + + return dst, nil } -func (s *srtpCipherAesCmHmacSha1) decryptRTCP(out, encrypted []byte, index, ssrc uint32) ([]byte, error) { +func (s *srtpCipherAesCmHmacSha1) decryptRTCP(dst, encrypted []byte, index, ssrc uint32) ([]byte, error) { authTagLen, err := s.AuthTagRTCPLen() if err != nil { return nil, err } - tailOffset := len(encrypted) - (authTagLen + len(s.mki) + srtcpIndexSize) - if tailOffset < 8 { + mkiLen := len(s.mki) + encryptedLen := len(encrypted) + decryptedLen := encryptedLen - (authTagLen + mkiLen + srtcpIndexSize) + if decryptedLen < 8 { return nil, errTooShortRTCP } - out = out[0:tailOffset] - expectedTag, err := s.generateSrtcpAuthTag(encrypted[:len(encrypted)-len(s.mki)-authTagLen]) + expectedTag, err := s.generateSrtcpAuthTag(encrypted[:encryptedLen-mkiLen-authTagLen]) if err != nil { return nil, err } - actualTag := encrypted[len(encrypted)-authTagLen:] + actualTag := encrypted[encryptedLen-authTagLen:] if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 { return nil, ErrFailedToVerifyAuthTag } - isEncrypted := encrypted[tailOffset]>>7 != 0 - if isEncrypted { - counter := generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt) - err = xorBytesCTR(s.srtcpBlock, counter[:], out[8:], out[8:]) - } else { - copy(out[8:], encrypted[8:]) + dst = growBufferSize(dst, decryptedLen) + sameBuffer := isSameBuffer(dst, encrypted) + + if !sameBuffer { + copy(dst, encrypted[:srtcpHeaderSize]) // Copy the first 8 bytes (RTCP header) } - return out, err + isEncrypted := encrypted[decryptedLen]&srtcpEncryptionFlag != 0 + if isEncrypted { + counter := generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt) //nolint:gosec // G115 + err = xorBytesCTR(s.srtcpBlock, counter[:], dst[srtcpHeaderSize:], encrypted[srtcpHeaderSize:decryptedLen]) + } else if !sameBuffer { + copy(dst[srtcpHeaderSize:], encrypted[srtcpHeaderSize:]) + } + + return dst, err } -func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([]byte, error) { +func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32, rocInAuthTag bool) ([]byte, error) { // https://tools.ietf.org/html/rfc3711#section-4.2 // In the case of SRTP, M SHALL consist of the Authenticated // Portion of the packet (as specified in Figure 1) concatenated with @@ -279,7 +337,13 @@ func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([ if err != nil { return nil, err } - return s.srtpSessionAuth.Sum(nil)[0:authTagLen], nil + + var authTag []byte + if rocInAuthTag { + authTag = append(authTag, rocRaw[:]...) + } + + return s.srtpSessionAuth.Sum(authTag)[0:authTagLen], nil } func (s *srtpCipherAesCmHmacSha1) generateSrtcpAuthTag(buf []byte) ([]byte, error) { @@ -311,21 +375,6 @@ func (s *srtpCipherAesCmHmacSha1) getRTCPIndex(in []byte) uint32 { authTagLen, _ := s.AuthTagRTCPLen() tailOffset := len(in) - (authTagLen + srtcpIndexSize + len(s.mki)) srtcpIndexBuffer := in[tailOffset : tailOffset+srtcpIndexSize] + return binary.BigEndian.Uint32(srtcpIndexBuffer) &^ (1 << 31) } - -func (s *srtpCipherAesCmHmacSha1) getMKI(in []byte, rtp bool) []byte { - mkiLen := len(s.mki) - if mkiLen == 0 { - return nil - } - - var authTagLen int - if rtp { - authTagLen, _ = s.AuthTagRTPLen() - } else { - authTagLen, _ = s.AuthTagRTCPLen() - } - tailOffset := len(in) - (authTagLen + mkiLen) - return in[tailOffset : tailOffset+mkiLen] -} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtcp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtcp.go index dc71c40f3..8fe407f64 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtcp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtcp.go @@ -13,10 +13,10 @@ import ( "github.com/pion/transport/v3/packetio" ) -// Limit the buffer size to 100KB +// Limit the buffer size to 100KB. const srtcpBufferSize = 100 * 1000 -// ReadStreamSRTCP handles decryption for a single RTCP SSRC +// ReadStreamSRTCP handles decryption for a single RTCP SSRC. type ReadStreamSRTCP struct { mu sync.Mutex @@ -40,12 +40,12 @@ func (r *ReadStreamSRTCP) write(buf []byte) (n int, err error) { return n, err } -// Used by getOrCreateReadStream +// Used by getOrCreateReadStream. func newReadStreamSRTCP() readStream { return &ReadStreamSRTCP{} } -// ReadRTCP reads and decrypts full RTCP packet and its header from the nextConn +// ReadRTCP reads and decrypts full RTCP packet and its header from the nextConn. func (r *ReadStreamSRTCP) ReadRTCP(buf []byte) (int, *rtcp.Header, error) { n, err := r.Read(buf) if err != nil { @@ -61,7 +61,7 @@ func (r *ReadStreamSRTCP) ReadRTCP(buf []byte) (int, *rtcp.Header, error) { return n, header, nil } -// Read reads and decrypts full RTCP packet from the nextConn +// Read reads and decrypts full RTCP packet from the nextConn. func (r *ReadStreamSRTCP) Read(buf []byte) (int, error) { return r.buffer.Read(buf) } @@ -74,10 +74,11 @@ func (r *ReadStreamSRTCP) SetReadDeadline(t time.Time) error { }); ok { return b.SetReadDeadline(t) } + return nil } -// Close removes the ReadStream from the session and cleans up any associated state +// Close removes the ReadStream from the session and cleans up any associated state. func (r *ReadStreamSRTCP) Close() error { r.mu.Lock() defer r.mu.Unlock() @@ -96,6 +97,7 @@ func (r *ReadStreamSRTCP) Close() error { } r.session.removeReadStream(r.ssrc) + return nil } } @@ -128,17 +130,17 @@ func (r *ReadStreamSRTCP) init(child streamSession, ssrc uint32) error { return nil } -// GetSSRC returns the SSRC we are demuxing for +// GetSSRC returns the SSRC we are demuxing for. func (r *ReadStreamSRTCP) GetSSRC() uint32 { return r.ssrc } -// WriteStreamSRTCP is stream for a single Session that is used to encrypt RTCP +// WriteStreamSRTCP is stream for a single Session that is used to encrypt RTCP. type WriteStreamSRTCP struct { session *SessionSRTCP } -// WriteRTCP encrypts a RTCP header and its payload to the nextConn +// WriteRTCP encrypts a RTCP header and its payload to the nextConn. func (w *WriteStreamSRTCP) WriteRTCP(header *rtcp.Header, payload []byte) (int, error) { headerRaw, err := header.Marshal() if err != nil { @@ -148,7 +150,7 @@ func (w *WriteStreamSRTCP) WriteRTCP(header *rtcp.Header, payload []byte) (int, return w.session.write(append(headerRaw, payload...)) } -// Write encrypts and writes a full RTCP packets to the nextConn +// Write encrypts and writes a full RTCP packets to the nextConn. func (w *WriteStreamSRTCP) Write(b []byte) (int, error) { return w.session.write(b) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtp.go index cad0a38c1..1b34266c1 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/stream_srtp.go @@ -13,10 +13,10 @@ import ( "github.com/pion/transport/v3/packetio" ) -// Limit the buffer size to 1MB +// Limit the buffer size to 1MB. const srtpBufferSize = 1000 * 1000 -// ReadStreamSRTP handles decryption for a single RTP SSRC +// ReadStreamSRTP handles decryption for a single RTP SSRC. type ReadStreamSRTP struct { mu sync.Mutex @@ -29,7 +29,7 @@ type ReadStreamSRTP struct { buffer io.ReadWriteCloser } -// Used by getOrCreateReadStream +// Used by getOrCreateReadStream. func newReadStreamSRTP() readStream { return &ReadStreamSRTP{} } @@ -74,12 +74,12 @@ func (r *ReadStreamSRTP) write(buf []byte) (n int, err error) { return n, err } -// Read reads and decrypts full RTP packet from the nextConn +// Read reads and decrypts full RTP packet from the nextConn. func (r *ReadStreamSRTP) Read(buf []byte) (int, error) { return r.buffer.Read(buf) } -// ReadRTP reads and decrypts full RTP packet and its header from the nextConn +// ReadRTP reads and decrypts full RTP packet and its header from the nextConn. func (r *ReadStreamSRTP) ReadRTP(buf []byte) (int, *rtp.Header, error) { n, err := r.Read(buf) if err != nil { @@ -104,10 +104,11 @@ func (r *ReadStreamSRTP) SetReadDeadline(t time.Time) error { }); ok { return b.SetReadDeadline(t) } + return nil } -// Close removes the ReadStream from the session and cleans up any associated state +// Close removes the ReadStream from the session and cleans up any associated state. func (r *ReadStreamSRTP) Close() error { r.mu.Lock() defer r.mu.Unlock() @@ -126,26 +127,27 @@ func (r *ReadStreamSRTP) Close() error { } r.session.removeReadStream(r.ssrc) + return nil } } -// GetSSRC returns the SSRC we are demuxing for +// GetSSRC returns the SSRC we are demuxing for. func (r *ReadStreamSRTP) GetSSRC() uint32 { return r.ssrc } -// WriteStreamSRTP is stream for a single Session that is used to encrypt RTP +// WriteStreamSRTP is stream for a single Session that is used to encrypt RTP. type WriteStreamSRTP struct { session *SessionSRTP } -// WriteRTP encrypts a RTP packet and writes to the connection +// WriteRTP encrypts a RTP packet and writes to the connection. func (w *WriteStreamSRTP) WriteRTP(header *rtp.Header, payload []byte) (int, error) { return w.session.writeRTP(header, payload) } -// Write encrypts and writes a full RTP packets to the nextConn +// Write encrypts and writes a full RTP packets to the nextConn. func (w *WriteStreamSRTP) Write(b []byte) (int, error) { return w.session.write(b) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/util.go index 792175d96..84116014e 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/util.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/srtp/v3/util.go @@ -3,9 +3,11 @@ package srtp -import "bytes" +import ( + "unsafe" +) -// Grow the buffer size to the given number of bytes. +// growBufferSize grows the buffer size to the given number of bytes. func growBufferSize(buf []byte, size int) []byte { if size <= cap(buf) { return buf[:size] @@ -13,24 +15,25 @@ func growBufferSize(buf []byte, size int) []byte { buf2 := make([]byte, size) copy(buf2, buf) + return buf2 } -// Check if buffers match, if not allocate a new buffer and return it -func allocateIfMismatch(dst, src []byte) []byte { - if dst == nil { - dst = make([]byte, len(src)) - copy(dst, src) - } else if !bytes.Equal(dst, src) { // bytes.Equal returns on ref equality, no optimization needed - extraNeeded := len(src) - len(dst) - if extraNeeded > 0 { - dst = append(dst, make([]byte, extraNeeded)...) - } else if extraNeeded < 0 { - dst = dst[:len(dst)+extraNeeded] - } - - copy(dst, src) +// isSameBuffer returns true if slices a and b share the same underlying buffer. +func isSameBuffer(a, b []byte) bool { + // If both are nil, they are technically the same (no buffer) + if a == nil && b == nil { + return true } - return dst + // If either is nil, or both have 0 capacity, they can't share backing buffer + if cap(a) == 0 || cap(b) == 0 { + return false + } + + // Create a slice of length 1 from each if possible + aPtr := unsafe.Pointer(&a[:1][0]) // nolint:gosec + bPtr := unsafe.Pointer(&b[:1][0]) // nolint:gosec + + return aPtr == bPtr } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/.golangci.yml index e06de4d3c..120faf29b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/.golangci.yml +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/.golangci.yml @@ -1,6 +1,9 @@ # SPDX-FileCopyrightText: 2023 The Pion community # SPDX-License-Identifier: MIT +run: + timeout: 5m + linters-settings: govet: enable: @@ -16,23 +19,42 @@ linters-settings: recommendations: - 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 + - 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`. @@ -43,18 +65,17 @@ linters: - 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 - 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 @@ -62,9 +83,15 @@ linters: - 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 @@ -72,31 +99,22 @@ linters: - 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 + - 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 - - 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 + - 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 - - 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 + - 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 @@ -104,8 +122,7 @@ linters: - 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 + - 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! @@ -114,9 +131,12 @@ issues: exclude-dirs-use-default: false exclude-rules: # Allow complex tests and examples, better to be self contained - - path: (examples|main\.go|_test\.go) + - path: (examples|main\.go) linters: + - gocognit - forbidigo + - path: _test\.go + linters: - gocognit # Allow forbidden identifiers in CLI commands diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/README.md b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/README.md index a571c0ba9..9c8d7038e 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/README.md +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/README.md @@ -7,13 +7,13 @@

A toolkit for building TURN clients and servers in Go

Pion TURN - Slack Widget + join us on Discord Follow us on Bluesky
GitHub Workflow Status - Go Reference + Go Reference Coverage Status - Go Report Card + Go Report Card License: MIT


@@ -79,9 +79,9 @@ Yes. 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) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/client.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/client.go index 6e38ae109..68d40c866 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/client.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/client.go @@ -49,7 +49,7 @@ type ClientConfig struct { LoggerFactory logging.LoggerFactory } -// Client is a STUN server client +// Client is a STUN server client. type Client struct { conn net.PacketConn // Read-only net transport.Net // Read-only @@ -72,7 +72,8 @@ type Client struct { log logging.LeveledLogger // Read-only } -// NewClient returns a new Client instance. listeningAddress is the address and port to listen on, default "0.0.0.0:0" +// NewClient returns a new Client instance. listeningAddress is the address and port to listen on, +// default "0.0.0.0:0". func NewClient(config *ClientConfig) (*Client, error) { loggerFactory := config.LoggerFactory if loggerFactory == nil { @@ -119,7 +120,7 @@ func NewClient(config *ClientConfig) (*Client, error) { log.Debugf("Resolved TURN server %s to %s", config.TURNServerAddr, turnServ) } - c := &Client{ + client := &Client{ conn: config.Conn, stunServerAddr: stunServ, turnServerAddr: turnServ, @@ -133,25 +134,25 @@ func NewClient(config *ClientConfig) (*Client, error) { log: log, } - return c, nil + return client, nil } -// TURNServerAddr return the TURN server address +// TURNServerAddr return the TURN server address. func (c *Client) TURNServerAddr() net.Addr { return c.turnServerAddr } -// STUNServerAddr return the STUN server address +// STUNServerAddr return the STUN server address. func (c *Client) STUNServerAddr() net.Addr { return c.stunServerAddr } -// Username returns username +// Username returns username. func (c *Client) Username() stun.Username { return c.username } -// Realm return realm +// Realm return realm. func (c *Client) Realm() stun.Realm { return c.realm } @@ -175,12 +176,14 @@ func (c *Client) Listen() error { n, from, err := c.conn.ReadFrom(buf) if err != nil { c.log.Debugf("Failed to read: %s. Exiting loop", err) + break } _, err = c.HandleInbound(buf[:n], from) if err != nil { c.log.Debugf("Failed to handle inbound message: %s. Exiting loop", err) + break } } @@ -191,7 +194,7 @@ func (c *Client) Listen() error { return nil } -// Close closes this client +// Close closes this client. func (c *Client) Close() { c.mutexTrMap.Lock() defer c.mutexTrMap.Unlock() @@ -201,7 +204,7 @@ func (c *Client) Close() { // TransactionID & Base64: https://play.golang.org/p/EEgmJDI971P -// SendBindingRequestTo sends a new STUN request to the given transport address +// SendBindingRequestTo sends a new STUN request to the given transport address. func (c *Client) SendBindingRequestTo(to net.Addr) (net.Addr, error) { attrs := []stun.Setter{stun.TransactionID, stun.BindingRequest} if len(c.software) > 0 { @@ -228,15 +231,21 @@ func (c *Client) SendBindingRequestTo(to net.Addr) (net.Addr, error) { }, nil } -// SendBindingRequest sends a new STUN request to the STUN server +// SendBindingRequest sends a new STUN request to the STUN server. func (c *Client) SendBindingRequest() (net.Addr, error) { if c.stunServerAddr == nil { return nil, errSTUNServerAddressNotSet } + return c.SendBindingRequestTo(c.stunServerAddr) } -func (c *Client) sendAllocateRequest(protocol proto.Protocol) (proto.RelayedAddress, proto.Lifetime, stun.Nonce, error) { +func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop + proto.RelayedAddress, + proto.Lifetime, + stun.Nonce, + error, +) { var relayed proto.RelayedAddress var lifetime proto.Lifetime var nonce stun.Nonce @@ -295,6 +304,7 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) (proto.RelayedAddr if err = code.GetFrom(res); err == nil { return relayed, lifetime, nonce, fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113 } + return relayed, lifetime, nonce, fmt.Errorf("%s", res.Type) //nolint:goerr113 } @@ -307,10 +317,11 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) (proto.RelayedAddr if err := lifetime.GetFrom(res); err != nil { return relayed, lifetime, nonce, err } + return relayed, lifetime, nonce, nil } -// Allocate sends a TURN allocation request to the given transport address +// Allocate sends a TURN allocation request to the given transport address. func (c *Client) Allocate() (net.PacketConn, error) { if err := c.allocTryLock.Lock(); err != nil { return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error()) @@ -403,10 +414,11 @@ func (c *Client) CreatePermission(addrs ...net.Addr) error { return err } } + return nil } -// PerformTransaction performs STUN transaction +// PerformTransaction performs STUN transaction. func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult bool) (client.TransactionResult, error, ) { @@ -442,11 +454,12 @@ func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult if res.Err != nil { return res, res.Err } + return res, nil } // OnDeallocated is called when de-allocation of relay address has been complete. -// (Called by UDPConn) +// (Called by UDPConn). func (c *Client) OnDeallocated(net.Addr) { c.setRelayedUDPConn(nil) c.setTCPAllocation(nil) @@ -494,7 +507,7 @@ func (c *Client) HandleInbound(data []byte, from net.Addr) (bool, error) { return false, nil } -func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { +func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { //nolint:cyclop raw := make([]byte, len(data)) copy(raw, data) @@ -507,7 +520,7 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { return fmt.Errorf("%w : %s", errUnexpectedSTUNRequestMessage, msg.String()) } - if msg.Type.Class == stun.ClassIndication { + if msg.Type.Class == stun.ClassIndication { // nolint:nestif switch msg.Type.Method { case stun.MethodData: var peerAddr proto.PeerAddress @@ -529,6 +542,7 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { relayedConn := c.relayedUDPConn() if relayedConn == nil { c.log.Debug("No relayed conn allocated") + return nil // Silently discard } relayedConn.HandleInbound(data, from) @@ -553,6 +567,7 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { allocation := c.getTCPAllocation() if allocation == nil { c.log.Debug("No TCP allocation exists") + return nil // Silently discard } @@ -560,6 +575,7 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { default: c.log.Debug("Received unsupported STUN method") } + return nil } @@ -576,6 +592,7 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error { c.mutexTrMap.Unlock() // Silently discard c.log.Debugf("No transaction for %s", msg) + return nil } @@ -607,6 +624,7 @@ func (c *Client) handleChannelData(data []byte) error { relayedConn := c.relayedUDPConn() if relayedConn == nil { c.log.Debug("No relayed conn allocated") + return nil // Silently discard } @@ -618,6 +636,7 @@ func (c *Client) handleChannelData(data []byte) error { c.log.Tracef("Channel data received from %s (ch=%d)", addr.String(), int(chData.Number)) relayedConn.HandleInbound(chData.Data, addr) + return nil } @@ -638,11 +657,12 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) { }) { c.log.Debug("No listener for transaction") } + return } c.log.Tracef("Retransmitting transaction %s to %s (nRtx=%d)", - trKey, tr.To.String(), nRtx) + trKey, tr.To, nRtx) _, err := c.conn.WriteTo(tr.Raw, tr.To) if err != nil { c.trMap.Delete(trKey) @@ -651,6 +671,7 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) { }) { c.log.Debug("No listener for transaction") } + return } tr.StartRtxTimer(c.onRtxTimeout) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go index 5b5ff369b..9e2d53521 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation.go @@ -22,7 +22,7 @@ type allocationResponse struct { } // Allocation is tied to a FiveTuple and relays traffic -// use CreateAllocation and GetAllocation to operate +// use CreateAllocation and GetAllocation to operate. type Allocation struct { RelayAddr net.Addr Protocol Protocol @@ -55,7 +55,7 @@ func NewAllocation(turnSocket net.PacketConn, fiveTuple *FiveTuple, log logging. } } -// GetPermission gets the Permission from the allocation +// GetPermission gets the Permission from the allocation. func (a *Allocation) GetPermission(addr net.Addr) *Permission { a.permissionsLock.RLock() defer a.permissionsLock.RUnlock() @@ -63,9 +63,9 @@ func (a *Allocation) GetPermission(addr net.Addr) *Permission { return a.permissions[ipnet.FingerprintAddr(addr)] } -// AddPermission adds a new permission to the allocation -func (a *Allocation) AddPermission(p *Permission) { - fingerprint := ipnet.FingerprintAddr(p.Addr) +// AddPermission adds a new permission to the allocation. +func (a *Allocation) AddPermission(perms *Permission) { + fingerprint := ipnet.FingerprintAddr(perms.Addr) a.permissionsLock.RLock() existedPermission, ok := a.permissions[fingerprint] @@ -73,18 +73,19 @@ func (a *Allocation) AddPermission(p *Permission) { if ok { existedPermission.refresh(permissionTimeout) + return } - p.allocation = a + perms.allocation = a a.permissionsLock.Lock() - a.permissions[fingerprint] = p + a.permissions[fingerprint] = perms a.permissionsLock.Unlock() - p.start(permissionTimeout) + perms.start(permissionTimeout) } -// RemovePermission removes the net.Addr's fingerprint from the allocation's permissions +// RemovePermission removes the net.Addr's fingerprint from the allocation's permissions. func (a *Allocation) RemovePermission(addr net.Addr) { a.permissionsLock.Lock() defer a.permissionsLock.Unlock() @@ -92,13 +93,13 @@ func (a *Allocation) RemovePermission(addr net.Addr) { } // AddChannelBind adds a new ChannelBind to the allocation, it also updates the -// permissions needed for this ChannelBind -func (a *Allocation) AddChannelBind(c *ChannelBind, lifetime time.Duration) error { +// permissions needed for this ChannelBind. +func (a *Allocation) AddChannelBind(chanBind *ChannelBind, lifetime time.Duration) error { // Check that this channel id isn't bound to another transport address, and // that this transport address isn't bound to another channel number. - channelByNumber := a.GetChannelByNumber(c.Number) + channelByNumber := a.GetChannelByNumber(chanBind.Number) - if channelByNumber != a.GetChannelByAddr(c.Peer) { + if channelByNumber != a.GetChannelByAddr(chanBind.Peer) { return errSameChannelDifferentPeer } @@ -107,12 +108,12 @@ func (a *Allocation) AddChannelBind(c *ChannelBind, lifetime time.Duration) erro a.channelBindingsLock.Lock() defer a.channelBindingsLock.Unlock() - c.allocation = a - a.channelBindings = append(a.channelBindings, c) - c.start(lifetime) + chanBind.allocation = a + a.channelBindings = append(a.channelBindings, chanBind) + chanBind.start(lifetime) // Channel binds also refresh permissions. - a.AddPermission(NewPermission(c.Peer, a.log)) + a.AddPermission(NewPermission(chanBind.Peer, a.log)) } else { channelByNumber.refresh(lifetime) @@ -123,7 +124,7 @@ func (a *Allocation) AddChannelBind(c *ChannelBind, lifetime time.Duration) erro return nil } -// RemoveChannelBind removes the ChannelBind from this allocation by id +// RemoveChannelBind removes the ChannelBind from this allocation by id. func (a *Allocation) RemoveChannelBind(number proto.ChannelNumber) bool { a.channelBindingsLock.Lock() defer a.channelBindingsLock.Unlock() @@ -131,6 +132,7 @@ func (a *Allocation) RemoveChannelBind(number proto.ChannelNumber) bool { for i := len(a.channelBindings) - 1; i >= 0; i-- { if a.channelBindings[i].Number == number { a.channelBindings = append(a.channelBindings[:i], a.channelBindings[i+1:]...) + return true } } @@ -138,7 +140,7 @@ func (a *Allocation) RemoveChannelBind(number proto.ChannelNumber) bool { return false } -// GetChannelByNumber gets the ChannelBind from this allocation by id +// GetChannelByNumber gets the ChannelBind from this allocation by id. func (a *Allocation) GetChannelByNumber(number proto.ChannelNumber) *ChannelBind { a.channelBindingsLock.RLock() defer a.channelBindingsLock.RUnlock() @@ -147,10 +149,11 @@ func (a *Allocation) GetChannelByNumber(number proto.ChannelNumber) *ChannelBind return cb } } + return nil } -// GetChannelByAddr gets the ChannelBind from this allocation by net.Addr +// GetChannelByAddr gets the ChannelBind from this allocation by net.Addr. func (a *Allocation) GetChannelByAddr(addr net.Addr) *ChannelBind { a.channelBindingsLock.RLock() defer a.channelBindingsLock.RUnlock() @@ -159,17 +162,18 @@ func (a *Allocation) GetChannelByAddr(addr net.Addr) *ChannelBind { return cb } } + return nil } -// Refresh updates the allocations lifetime +// Refresh updates the allocations lifetime. func (a *Allocation) Refresh(lifetime time.Duration) { if !a.lifetimeTimer.Reset(lifetime) { a.log.Errorf("Failed to reset allocation timer for %v", a.fiveTuple) } } -// SetResponseCache cache allocation response for retransmit allocation request +// SetResponseCache cache allocation response for retransmit allocation request. func (a *Allocation) SetResponseCache(transactionID [stun.TransactionIDSize]byte, attrs []stun.Setter) { a.responseCache.Store(&allocationResponse{ transactionID: transactionID, @@ -177,15 +181,16 @@ func (a *Allocation) SetResponseCache(transactionID [stun.TransactionIDSize]byte }) } -// GetResponseCache return response cache for retransmit allocation request +// GetResponseCache return response cache for retransmit allocation request. func (a *Allocation) GetResponseCache() (id [stun.TransactionIDSize]byte, attrs []stun.Setter) { if res, ok := a.responseCache.Load().(*allocationResponse); ok && res != nil { id, attrs = res.transactionID, res.responseAttrs } + return } -// Close closes the allocation +// Close closes the allocation. func (a *Allocation) Close() error { select { case <-a.closed: @@ -233,13 +238,14 @@ func (a *Allocation) Close() error { const rtpMTU = 1600 -func (a *Allocation) packetHandler(m *Manager) { +func (a *Allocation) packetHandler(manager *Manager) { buffer := make([]byte, rtpMTU) for { n, srcAddr, err := a.RelaySocket.ReadFrom(buffer) if err != nil { - m.DeleteAllocation(a.fiveTuple) + manager.DeleteAllocation(a.fiveTuple) + return } @@ -248,7 +254,7 @@ func (a *Allocation) packetHandler(m *Manager) { n, srcAddr) - if channel := a.GetChannelByAddr(srcAddr); channel != nil { + if channel := a.GetChannelByAddr(srcAddr); channel != nil { // nolint:nestif channelData := &proto.ChannelData{ Data: buffer[:n], Number: channel.Number, @@ -262,15 +268,22 @@ func (a *Allocation) packetHandler(m *Manager) { udpAddr, ok := srcAddr.(*net.UDPAddr) if !ok { a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err) + return } peerAddressAttr := proto.PeerAddress{IP: udpAddr.IP, Port: udpAddr.Port} dataAttr := proto.Data(buffer[:n]) - msg, err := stun.Build(stun.TransactionID, stun.NewType(stun.MethodData, stun.ClassIndication), peerAddressAttr, dataAttr) + msg, err := stun.Build( + stun.TransactionID, + stun.NewType(stun.MethodData, stun.ClassIndication), + peerAddressAttr, + dataAttr, + ) if err != nil { a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err) + return } a.log.Debugf("Relaying message from %s to client at %s", diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go index 2b7659212..a3b011f4e 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/allocation_manager.go @@ -25,7 +25,7 @@ type reservation struct { port int } -// Manager is used to hold active allocations +// Manager is used to hold active allocations. type Manager struct { lock sync.RWMutex log logging.LeveledLogger @@ -58,21 +58,23 @@ func NewManager(config ManagerConfig) (*Manager, error) { }, nil } -// GetAllocation fetches the allocation matching the passed FiveTuple +// GetAllocation fetches the allocation matching the passed FiveTuple. func (m *Manager) GetAllocation(fiveTuple *FiveTuple) *Allocation { m.lock.RLock() defer m.lock.RUnlock() + return m.allocations[fiveTuple.Fingerprint()] } -// AllocationCount returns the number of existing allocations +// AllocationCount returns the number of existing allocations. func (m *Manager) AllocationCount() int { m.lock.RLock() defer m.lock.RUnlock() + return len(m.allocations) } -// Close closes the manager and closes all allocations it manages +// Close closes the manager and closes all allocations it manages. func (m *Manager) Close() error { m.lock.Lock() defer m.lock.Unlock() @@ -82,11 +84,17 @@ func (m *Manager) Close() error { return err } } + return nil } -// CreateAllocation creates a new allocation and starts relaying -func (m *Manager) CreateAllocation(fiveTuple *FiveTuple, turnSocket net.PacketConn, requestedPort int, lifetime time.Duration) (*Allocation, error) { +// CreateAllocation creates a new allocation and starts relaying. +func (m *Manager) CreateAllocation( + fiveTuple *FiveTuple, + turnSocket net.PacketConn, + requestedPort int, + lifetime time.Duration, +) (*Allocation, error) { switch { case fiveTuple == nil: return nil, errNilFiveTuple @@ -100,34 +108,35 @@ func (m *Manager) CreateAllocation(fiveTuple *FiveTuple, turnSocket net.PacketCo return nil, errLifetimeZero } - if a := m.GetAllocation(fiveTuple); a != nil { + if alloc := m.GetAllocation(fiveTuple); alloc != nil { return nil, fmt.Errorf("%w: %v", errDupeFiveTuple, fiveTuple) } - a := NewAllocation(turnSocket, fiveTuple, m.log) + alloc := NewAllocation(turnSocket, fiveTuple, m.log) conn, relayAddr, err := m.allocatePacketConn("udp4", requestedPort) if err != nil { return nil, err } - a.RelaySocket = conn - a.RelayAddr = relayAddr + alloc.RelaySocket = conn + alloc.RelayAddr = relayAddr - m.log.Debugf("Listening on relay address: %s", a.RelayAddr) + m.log.Debugf("Listening on relay address: %s", alloc.RelayAddr) - a.lifetimeTimer = time.AfterFunc(lifetime, func() { - m.DeleteAllocation(a.fiveTuple) + alloc.lifetimeTimer = time.AfterFunc(lifetime, func() { + m.DeleteAllocation(alloc.fiveTuple) }) m.lock.Lock() - m.allocations[fiveTuple.Fingerprint()] = a + m.allocations[fiveTuple.Fingerprint()] = alloc m.lock.Unlock() - go a.packetHandler(m) - return a, nil + go alloc.packetHandler(m) + + return alloc, nil } -// DeleteAllocation removes an allocation +// DeleteAllocation removes an allocation. func (m *Manager) DeleteAllocation(fiveTuple *FiveTuple) { fingerprint := fiveTuple.Fingerprint() @@ -145,7 +154,7 @@ func (m *Manager) DeleteAllocation(fiveTuple *FiveTuple) { } } -// CreateReservation stores the reservation for the token+port +// CreateReservation stores the reservation for the token+port. func (m *Manager) CreateReservation(reservationToken string, port int) { time.AfterFunc(30*time.Second, func() { m.lock.Lock() @@ -153,6 +162,7 @@ func (m *Manager) CreateReservation(reservationToken string, port int) { for i := len(m.reservations) - 1; i >= 0; i-- { if m.reservations[i].token == reservationToken { m.reservations = append(m.reservations[:i], m.reservations[i+1:]...) + return } } @@ -166,7 +176,7 @@ func (m *Manager) CreateReservation(reservationToken string, port int) { m.lock.Unlock() } -// GetReservation returns the port for a given reservation if it exists +// GetReservation returns the port for a given reservation if it exists. func (m *Manager) GetReservation(reservationToken string) (int, bool) { m.lock.RLock() defer m.lock.RUnlock() @@ -176,10 +186,11 @@ func (m *Manager) GetReservation(reservationToken string) (int, bool) { return r.port, true } } + return 0, false } -// GetRandomEvenPort returns a random un-allocated udp4 port +// GetRandomEvenPort returns a random un-allocated udp4 port. func (m *Manager) GetRandomEvenPort() (int, error) { for i := 0; i < 128; i++ { conn, addr, err := m.allocatePacketConn("udp4", 0) @@ -199,11 +210,12 @@ func (m *Manager) GetRandomEvenPort() (int, error) { return udpAddr.Port, nil } } + return 0, errFailedToAllocateEvenPort } // GrantPermission handles permission requests by calling the permission handler callback -// associated with the TURN server listener socket +// associated with the TURN server listener socket. func (m *Manager) GrantPermission(sourceAddr net.Addr, peerIP net.IP) error { // No permission handler: open if m.permissionHandler == nil { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go index 19b184317..6ad9b46f0 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/channel_bind.go @@ -22,7 +22,7 @@ type ChannelBind struct { log logging.LeveledLogger } -// NewChannelBind creates a new ChannelBind +// NewChannelBind creates a new ChannelBind. func NewChannelBind(number proto.ChannelNumber, peer net.Addr, log logging.LeveledLogger) *ChannelBind { return &ChannelBind{ Number: number, diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go index 6d812caf7..14761611d 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/five_tuple.go @@ -7,10 +7,10 @@ import ( "net" ) -// Protocol is an enum for relay protocol +// Protocol is an enum for relay protocol. type Protocol uint8 -// Network protocols for relay +// Network protocols for relay. const ( UDP Protocol = iota TCP @@ -27,19 +27,19 @@ type FiveTuple struct { SrcAddr, DstAddr net.Addr } -// Equal asserts if two FiveTuples are equal +// Equal asserts if two FiveTuples are equal. func (f *FiveTuple) Equal(b *FiveTuple) bool { return f.Fingerprint() == b.Fingerprint() } -// FiveTupleFingerprint is a comparable representation of a FiveTuple +// FiveTupleFingerprint is a comparable representation of a FiveTuple. type FiveTupleFingerprint struct { srcIP, dstIP [16]byte srcPort, dstPort uint16 protocol Protocol } -// Fingerprint is the identity of a FiveTuple +// Fingerprint is the identity of a FiveTuple. func (f *FiveTuple) Fingerprint() (fp FiveTupleFingerprint) { srcIP, srcPort := netAddrIPAndPort(f.SrcAddr) copy(fp.srcIP[:], srcIP) @@ -48,15 +48,16 @@ func (f *FiveTuple) Fingerprint() (fp FiveTupleFingerprint) { copy(fp.dstIP[:], dstIP) fp.dstPort = dstPort fp.protocol = f.Protocol + return } func netAddrIPAndPort(addr net.Addr) (net.IP, uint16) { switch a := addr.(type) { case *net.UDPAddr: - return a.IP.To16(), uint16(a.Port) + return a.IP.To16(), uint16(a.Port) // nolint:gosec // G115 case *net.TCPAddr: - return a.IP.To16(), uint16(a.Port) + return a.IP.To16(), uint16(a.Port) // nolint:gosec // G115 default: return nil, 0 } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/permission.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/permission.go index a774f2ceb..7b02adc35 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/permission.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/allocation/permission.go @@ -22,7 +22,7 @@ type Permission struct { log logging.LeveledLogger } -// NewPermission create a new Permission +// NewPermission create a new Permission. func NewPermission(addr net.Addr, log logging.LeveledLogger) *Permission { return &Permission{ Addr: addr, diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/allocation.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/allocation.go index a7f746360..ed64eb879 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/allocation.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/allocation.go @@ -16,7 +16,7 @@ import ( "github.com/pion/turn/v4/internal/proto" ) -// AllocationConfig is a set of configuration params use by NewUDPConn and NewTCPAllocation +// AllocationConfig is a set of configuration params use by NewUDPConn and NewTCPAllocation. type AllocationConfig struct { Client Client RelayedAddr net.Addr @@ -82,6 +82,7 @@ func (a *allocation) refreshAllocation(lifetime time.Duration, dontWait bool) er if dontWait { a.log.Debug("Refresh request sent") + return nil } @@ -93,10 +94,13 @@ func (a *allocation) refreshAllocation(lifetime time.Duration, dontWait bool) er if err = code.GetFrom(res); err == nil { if code.Code == stun.CodeStaleNonce { a.setNonceFromMsg(res) + return errTryAgain } + return err } + return fmt.Errorf("%s", res.Type) //nolint:goerr113 } @@ -108,6 +112,7 @@ func (a *allocation) refreshAllocation(lifetime time.Duration, dontWait bool) er a.setLifetime(updatedLifetime.Duration) a.log.Debugf("Updated lifetime: %d seconds", int(a.lifetime().Seconds())) + return nil } @@ -115,6 +120,7 @@ func (a *allocation) refreshPermissions() error { addrs := a.permMap.addrs() if len(addrs) == 0 { a.log.Debug("No permission to refresh") + return nil } if err := a.CreatePermissions(addrs...); err != nil { @@ -122,9 +128,11 @@ func (a *allocation) refreshPermissions() error { return errTryAgain } a.log.Errorf("Fail to refresh permissions: %s", err) + return err } a.log.Debug("Refresh permissions successful") + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/binding.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/binding.go index a0217476e..1203cceb4 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/binding.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/binding.go @@ -61,7 +61,7 @@ func (b *binding) refreshedAt() time.Time { return b._refreshedAt } -// Thread-safe binding map +// Thread-safe binding map. type bindingManager struct { chanMap map[uint16]*binding addrMap map[string]*binding @@ -84,6 +84,7 @@ func (mgr *bindingManager) assignChannelNumber() uint16 { } else { mgr.next++ } + return n } @@ -100,6 +101,7 @@ func (mgr *bindingManager) create(addr net.Addr) *binding { mgr.chanMap[b.number] = b mgr.addrMap[b.addr.String()] = b + return b } @@ -108,6 +110,7 @@ func (mgr *bindingManager) findByAddr(addr net.Addr) (*binding, bool) { defer mgr.mutex.RUnlock() b, ok := mgr.addrMap[addr.String()] + return b, ok } @@ -116,6 +119,7 @@ func (mgr *bindingManager) findByNumber(number uint16) (*binding, bool) { defer mgr.mutex.RUnlock() b, ok := mgr.chanMap[number] + return b, ok } @@ -130,6 +134,7 @@ func (mgr *bindingManager) deleteByAddr(addr net.Addr) bool { delete(mgr.addrMap, addr.String()) delete(mgr.chanMap, b.number) + return true } @@ -144,6 +149,7 @@ func (mgr *bindingManager) deleteByNumber(number uint16) bool { delete(mgr.addrMap, b.addr.String()) delete(mgr.chanMap, number) + return true } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/client.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/client.go index 10b49a284..45041c904 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/client.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/client.go @@ -10,7 +10,7 @@ import ( "github.com/pion/stun/v3" ) -// Client is an interface for the public turn.Client in order to break cyclic dependencies +// Client is an interface for the public turn.Client in order to break cyclic dependencies. type Client interface { WriteTo(data []byte, to net.Addr) (int, error) PerformTransaction(msg *stun.Message, to net.Addr, dontWait bool) (TransactionResult, error) diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go index 5660bc45e..9e949129f 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/periodic_timer.go @@ -8,10 +8,10 @@ import ( "time" ) -// PeriodicTimerTimeoutHandler is a handler called on timeout +// PeriodicTimerTimeoutHandler is a handler called on timeout. type PeriodicTimerTimeoutHandler func(timerID int) -// PeriodicTimer is a periodic timer +// PeriodicTimer is a periodic timer. type PeriodicTimer struct { id int interval time.Duration @@ -20,7 +20,7 @@ type PeriodicTimer struct { mutex sync.RWMutex } -// NewPeriodicTimer create a new timer +// NewPeriodicTimer create a new timer. func NewPeriodicTimer(id int, timeoutHandler PeriodicTimerTimeoutHandler, interval time.Duration) *PeriodicTimer { return &PeriodicTimer{ id: id, @@ -76,7 +76,7 @@ func (t *PeriodicTimer) Stop() { } // IsRunning tests if the timer is running. -// Debug purpose only +// Debug purpose only. func (t *PeriodicTimer) IsRunning() bool { t.mutex.RLock() defer t.mutex.RUnlock() diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/permission.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/permission.go index 0436e4ea6..d6708d861 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/permission.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/permission.go @@ -32,7 +32,7 @@ func (p *permission) state() permState { return permState(atomic.LoadInt32((*int32)(&p.st))) } -// Thread-safe permission map +// Thread-safe permission map. type permissionMap struct { permMap map[string]*permission mutex sync.RWMutex @@ -43,6 +43,7 @@ func (m *permissionMap) insert(addr net.Addr, p *permission) bool { defer m.mutex.Unlock() p.addr = addr m.permMap[ipnet.FingerprintAddr(addr)] = p + return true } @@ -50,6 +51,7 @@ func (m *permissionMap) find(addr net.Addr) (*permission, bool) { m.mutex.RLock() defer m.mutex.RUnlock() p, ok := m.permMap[ipnet.FingerprintAddr(addr)] + return p, ok } @@ -67,6 +69,7 @@ func (m *permissionMap) addrs() []net.Addr { for _, p := range m.permMap { addrs = append(addrs, p.addr) } + return addrs } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go index 82948387c..5b72fe801 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_alloc.go @@ -34,9 +34,9 @@ type TCPAllocation struct { allocation } -// NewTCPAllocation creates a new instance of TCPConn +// NewTCPAllocation creates a new instance of TCPConn. func NewTCPAllocation(config *AllocationConfig) *TCPAllocation { - a := &TCPAllocation{ + alloc := &TCPAllocation{ connAttemptCh: make(chan *connectionAttempt, 10), acceptTimer: time.NewTimer(time.Duration(math.MaxInt64)), allocation: allocation{ @@ -54,31 +54,31 @@ func NewTCPAllocation(config *AllocationConfig) *TCPAllocation { }, } - a.log.Debugf("Initial lifetime: %d seconds", int(a.lifetime().Seconds())) + alloc.log.Debugf("Initial lifetime: %d seconds", int(alloc.lifetime().Seconds())) - a.refreshAllocTimer = NewPeriodicTimer( + alloc.refreshAllocTimer = NewPeriodicTimer( timerIDRefreshAlloc, - a.onRefreshTimers, - a.lifetime()/2, + alloc.onRefreshTimers, + alloc.lifetime()/2, ) - a.refreshPermsTimer = NewPeriodicTimer( + alloc.refreshPermsTimer = NewPeriodicTimer( timerIDRefreshPerms, - a.onRefreshTimers, + alloc.onRefreshTimers, permRefreshInterval, ) - if a.refreshAllocTimer.Start() { - a.log.Debug("Started refreshAllocTimer") + if alloc.refreshAllocTimer.Start() { + alloc.log.Debug("Started refreshAllocTimer") } - if a.refreshPermsTimer.Start() { - a.log.Debug("Started refreshPermsTimer") + if alloc.refreshPermsTimer.Start() { + alloc.log.Debug("Started refreshPermsTimer") } - return a + return alloc } -// Connect sends a Connect request to the turn server and returns a chosen connection ID +// Connect sends a Connect request to the turn server and returns a chosen connection ID. func (a *TCPAllocation) Connect(peer net.Addr) (proto.ConnectionID, error) { setters := []stun.Setter{ stun.TransactionID, @@ -119,6 +119,7 @@ func (a *TCPAllocation) Connect(peer net.Addr) (proto.ConnectionID, error) { } a.log.Debugf("Connect request successful (cid=%v)", cid) + return cid, nil } @@ -218,8 +219,8 @@ func (a *TCPAllocation) DialTCPWithConn(conn net.Conn, _ string, rAddr *net.TCPA return dataConn, nil } -// BindConnection associates the provided connection -func (a *TCPAllocation) BindConnection(dataConn *TCPConn, cid proto.ConnectionID) error { +// BindConnection associates the provided connection. +func (a *TCPAllocation) BindConnection(dataConn *TCPConn, cid proto.ConnectionID) error { //nolint:cyclop msg, err := stun.Build( stun.TransactionID, stun.NewType(stun.MethodConnectionBind, stun.ClassRequest), @@ -272,9 +273,11 @@ func (a *TCPAllocation) BindConnection(dataConn *TCPConn, cid proto.ConnectionID if err = code.GetFrom(res); err == nil { return fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113 } + return fmt.Errorf("%s", res.Type) //nolint:goerr113 case stun.ClassSuccessResponse: a.log.Debug("Successful connectionBind request") + return nil default: return fmt.Errorf("%w: %s", errUnexpectedSTUNRequestMessage, res.String()) @@ -347,6 +350,7 @@ func (a *TCPAllocation) SetDeadline(t time.Time) error { d = time.Until(t) } a.acceptTimer.Reset(d) + return nil } @@ -358,10 +362,11 @@ func (a *TCPAllocation) Close() error { a.refreshPermsTimer.Stop() a.client.OnDeallocated(a.relayedAddr) + return a.refreshAllocation(0, true /* dontWait=true */) } -// Addr returns the relayed address of the allocation +// Addr returns the relayed address of the allocation. func (a *TCPAllocation) Addr() net.Addr { return a.relayedAddr } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go index 990b6d424..18330becd 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/tcp_conn.go @@ -23,7 +23,7 @@ const ( var _ transport.TCPConn = (*TCPConn)(nil) // Includes type check for net.Conn // TCPConn wraps a transport.TCPConn and returns the allocations relayed -// transport address in response to TCPConn.LocalAddress() +// transport address in response to TCPConn.LocalAddress(). type TCPConn struct { transport.TCPConn remoteAddress *net.TCPAddr diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/transaction.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/transaction.go index b1c9105e6..744df6689 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/transaction.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/transaction.go @@ -15,7 +15,7 @@ const ( maxRtxInterval time.Duration = 1600 * time.Millisecond ) -// TransactionResult is a bag of result values of a transaction +// TransactionResult is a bag of result values of a transaction. type TransactionResult struct { Msg *stun.Message From net.Addr @@ -23,7 +23,7 @@ type TransactionResult struct { Err error } -// TransactionConfig is a set of config params used by NewTransaction +// TransactionConfig is a set of config params used by NewTransaction. type TransactionConfig struct { Key string Raw []byte @@ -32,7 +32,7 @@ type TransactionConfig struct { IgnoreResult bool // True to throw away the result of this transaction (it will not be readable using WaitForResult) } -// Transaction represents a transaction +// Transaction represents a transaction. type Transaction struct { Key string // Read-only Raw []byte // Read-only @@ -44,7 +44,7 @@ type Transaction struct { mutex sync.RWMutex } -// NewTransaction creates a new instance of Transaction +// NewTransaction creates a new instance of Transaction. func NewTransaction(config *TransactionConfig) *Transaction { var resultCh chan TransactionResult if !config.IgnoreResult { @@ -60,7 +60,7 @@ func NewTransaction(config *TransactionConfig) *Transaction { } } -// StartRtxTimer starts the transaction timer +// StartRtxTimer starts the transaction timer. func (t *Transaction) StartRtxTimer(onTimeout func(trKey string, nRtx int)) { t.mutex.Lock() defer t.mutex.Unlock() @@ -78,7 +78,7 @@ func (t *Transaction) StartRtxTimer(onTimeout func(trKey string, nRtx int)) { }) } -// StopRtxTimer stop the transaction timer +// StopRtxTimer stop the transaction timer. func (t *Transaction) StopRtxTimer() { t.mutex.Lock() defer t.mutex.Unlock() @@ -88,7 +88,7 @@ func (t *Transaction) StopRtxTimer() { } } -// WriteResult writes the result to the result channel +// WriteResult writes the result to the result channel. func (t *Transaction) WriteResult(res TransactionResult) bool { if t.resultCh == nil { return false @@ -99,7 +99,7 @@ func (t *Transaction) WriteResult(res TransactionResult) bool { return true } -// WaitForResult waits for the transaction result +// WaitForResult waits for the transaction result. func (t *Transaction) WaitForResult() TransactionResult { if t.resultCh == nil { return TransactionResult{ @@ -111,17 +111,18 @@ func (t *Transaction) WaitForResult() TransactionResult { if !ok { result.Err = errTransactionClosed } + return result } -// Close closes the transaction +// Close closes the transaction. func (t *Transaction) Close() { if t.resultCh != nil { close(t.resultCh) } } -// Retries returns the number of retransmission it has made +// Retries returns the number of retransmission it has made. func (t *Transaction) Retries() int { t.mutex.RLock() defer t.mutex.RUnlock() @@ -129,38 +130,40 @@ func (t *Transaction) Retries() int { return t.nRtx } -// TransactionMap is a thread-safe transaction map +// TransactionMap is a thread-safe transaction map. type TransactionMap struct { trMap map[string]*Transaction mutex sync.RWMutex } -// NewTransactionMap create a new instance of the transaction map +// NewTransactionMap create a new instance of the transaction map. func NewTransactionMap() *TransactionMap { return &TransactionMap{ trMap: map[string]*Transaction{}, } } -// Insert inserts a transaction to the map +// Insert inserts a transaction to the map. func (m *TransactionMap) Insert(key string, tr *Transaction) bool { m.mutex.Lock() defer m.mutex.Unlock() m.trMap[key] = tr + return true } -// Find looks up a transaction by its key +// Find looks up a transaction by its key. func (m *TransactionMap) Find(key string) (*Transaction, bool) { m.mutex.RLock() defer m.mutex.RUnlock() tr, ok := m.trMap[key] + return tr, ok } -// Delete deletes a transaction by its key +// Delete deletes a transaction by its key. func (m *TransactionMap) Delete(key string) { m.mutex.Lock() defer m.mutex.Unlock() @@ -168,7 +171,7 @@ func (m *TransactionMap) Delete(key string) { delete(m.trMap, key) } -// CloseAndDeleteAll closes and deletes all transactions +// CloseAndDeleteAll closes and deletes all transactions. func (m *TransactionMap) CloseAndDeleteAll() { m.mutex.Lock() defer m.mutex.Unlock() @@ -179,7 +182,7 @@ func (m *TransactionMap) CloseAndDeleteAll() { } } -// Size returns the length of the transaction map +// Size returns the length of the transaction map. func (m *TransactionMap) Size() int { m.mutex.RLock() defer m.mutex.RUnlock() diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/trylock.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/trylock.go index 6bfe6ff31..8380b0c8b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/trylock.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/trylock.go @@ -18,6 +18,7 @@ func (c *TryLock) Lock() error { if !atomic.CompareAndSwapInt32(&c.n, 0, 1) { return errDoubleLock } + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go index 7bb187659..59d10ceaf 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/client/udp_conn.go @@ -33,7 +33,7 @@ type inboundData struct { } // UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections. -// compatible with net.PacketConn and net.Conn +// compatible with net.PacketConn and net.Conn. type UDPConn struct { bindingMgr *bindingManager // Thread-safe readCh chan *inboundData // Thread-safe @@ -41,9 +41,9 @@ type UDPConn struct { allocation } -// NewUDPConn creates a new instance of UDPConn +// NewUDPConn creates a new instance of UDPConn. func NewUDPConn(config *AllocationConfig) *UDPConn { - c := &UDPConn{ + conn := &UDPConn{ bindingMgr: newBindingManager(), readCh: make(chan *inboundData, maxReadQueueSize), closeCh: make(chan struct{}), @@ -63,28 +63,28 @@ func NewUDPConn(config *AllocationConfig) *UDPConn { }, } - c.log.Debugf("Initial lifetime: %d seconds", int(c.lifetime().Seconds())) + conn.log.Debugf("Initial lifetime: %d seconds", int(conn.lifetime().Seconds())) - c.refreshAllocTimer = NewPeriodicTimer( + conn.refreshAllocTimer = NewPeriodicTimer( timerIDRefreshAlloc, - c.onRefreshTimers, - c.lifetime()/2, + conn.onRefreshTimers, + conn.lifetime()/2, ) - c.refreshPermsTimer = NewPeriodicTimer( + conn.refreshPermsTimer = NewPeriodicTimer( timerIDRefreshPerms, - c.onRefreshTimers, + conn.onRefreshTimers, permRefreshInterval, ) - if c.refreshAllocTimer.Start() { - c.log.Debugf("Started refresh allocation timer") + if conn.refreshAllocTimer.Start() { + conn.log.Debugf("Started refresh allocation timer") } - if c.refreshPermsTimer.Start() { - c.log.Debugf("Started refresh permission timer") + if conn.refreshPermsTimer.Start() { + conn.log.Debugf("Started refresh permission timer") } - return c + return conn } // ReadFrom reads a packet from the connection, @@ -105,6 +105,7 @@ func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { if n < len(ibData.data) { return 0, nil, io.ErrShortBuffer } + return n, ibData.from, nil case <-c.readTimer.C: @@ -134,19 +135,21 @@ func (a *allocation) createPermission(perm *permission, addr net.Addr) error { // Punch a hole! (this would block a bit..) if err := a.CreatePermissions(addr); err != nil { a.permMap.delete(addr) + return err } perm.setState(permStatePermitted) } + return nil } -// WriteTo writes a packet with payload p to addr. +// WriteTo writes a packet with payload to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; // see SetDeadline and SetWriteDeadline. // On packet-oriented connections, write timeouts are rare. -func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: gocognit +func (c *UDPConn) WriteTo(payload []byte, addr net.Addr) (int, error) { //nolint:gocognit,cyclop var err error _, ok := addr.(*net.UDPAddr) if !ok { @@ -177,31 +180,32 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco } // Bind channel - b, ok := c.bindingMgr.findByAddr(addr) + bound, ok := c.bindingMgr.findByAddr(addr) if !ok { - b = c.bindingMgr.create(addr) + bound = c.bindingMgr.create(addr) } - bindSt := b.state() + bindSt := bound.state() + //nolint:nestif if bindSt == bindingStateIdle || bindSt == bindingStateRequest || bindSt == bindingStateFailed { func() { // Block only callers with the same binding until // the binding transaction has been complete - b.muBind.Lock() - defer b.muBind.Unlock() + bound.muBind.Lock() + defer bound.muBind.Unlock() // Binding state may have been changed while waiting. check again. - if b.state() == bindingStateIdle { - b.setState(bindingStateRequest) + if bound.state() == bindingStateIdle { + bound.setState(bindingStateRequest) go func() { - err2 := c.bind(b) + err2 := c.bind(bound) if err2 != nil { c.log.Warnf("Failed to bind bind(): %s", err2) - b.setState(bindingStateFailed) + bound.setState(bindingStateFailed) // Keep going... } else { - b.setState(bindingStateReady) + bound.setState(bindingStateReady) } }() } @@ -213,7 +217,7 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco msg, err = stun.Build( stun.TransactionID, stun.NewType(stun.MethodSend, stun.ClassIndication), - proto.Data(p), + proto.Data(payload), peerAddr, stun.Fingerprint, ) @@ -230,31 +234,31 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco // Check if the binding needs a refresh func() { - b.muBind.Lock() - defer b.muBind.Unlock() + bound.muBind.Lock() + defer bound.muBind.Unlock() - if b.state() == bindingStateReady && time.Since(b.refreshedAt()) > 5*time.Minute { - b.setState(bindingStateRefresh) + if bound.state() == bindingStateReady && time.Since(bound.refreshedAt()) > 5*time.Minute { + bound.setState(bindingStateRefresh) go func() { - err = c.bind(b) - if err != nil { - c.log.Warnf("Failed to bind() for refresh: %s", err) - b.setState(bindingStateFailed) + if bindErr := c.bind(bound); bindErr != nil { + c.log.Warnf("Failed to bind() for refresh: %s", bindErr) + bound.setState(bindingStateFailed) // Keep going... } else { - b.setRefreshedAt(time.Now()) - b.setState(bindingStateReady) + bound.setRefreshedAt(time.Now()) + bound.setState(bindingStateReady) } }() } }() // Send via ChannelData - _, err = c.sendChannelData(p, b.number) + _, err = c.sendChannelData(payload, bound.number) if err != nil { return 0, err } - return len(p), nil + + return len(payload), nil } // Close closes the connection. @@ -271,6 +275,7 @@ func (c *UDPConn) Close() error { } c.client.OnDeallocated(c.relayedAddr) + return c.refreshAllocation(0, true /* dontWait=true */) } @@ -309,6 +314,7 @@ func (c *UDPConn) SetReadDeadline(t time.Time) error { d = time.Until(t) } c.readTimer.Reset(d) + return nil } @@ -372,17 +378,20 @@ func (a *allocation) CreatePermissions(addrs ...net.Addr) error { if err = code.GetFrom(res); err == nil { if code.Code == stun.CodeStaleNonce { a.setNonceFromMsg(res) + return errTryAgain } + return fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113 } + return fmt.Errorf("%s", res.Type) //nolint:goerr113 } return nil } -// HandleInbound passes inbound data in UDPConn +// HandleInbound passes inbound data in UDPConn. func (c *UDPConn) HandleInbound(data []byte, from net.Addr) { // Copy data copied := make([]byte, len(data)) @@ -396,21 +405,22 @@ func (c *UDPConn) HandleInbound(data []byte, from net.Addr) { } // FindAddrByChannelNumber returns a peer address associated with the -// channel number on this UDPConn +// channel number on this UDPConn. func (c *UDPConn) FindAddrByChannelNumber(chNum uint16) (net.Addr, bool) { b, ok := c.bindingMgr.findByNumber(chNum) if !ok { return nil, false } + return b.addr, true } -func (c *UDPConn) bind(b *binding) error { +func (c *UDPConn) bind(bound *binding) error { setters := []stun.Setter{ stun.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassRequest), - addr2PeerAddress(b.addr), - proto.ChannelNumber(b.number), + addr2PeerAddress(bound.addr), + proto.ChannelNumber(bound.number), c.username, c.realm, c.nonce(), @@ -425,7 +435,8 @@ func (c *UDPConn) bind(b *binding) error { trRes, err := c.client.PerformTransaction(msg, c.serverAddr, false) if err != nil { - c.bindingMgr.deleteByAddr(b.addr) + c.bindingMgr.deleteByAddr(bound.addr) + return err } @@ -435,7 +446,7 @@ func (c *UDPConn) bind(b *binding) error { return fmt.Errorf("unexpected response type %s", res.Type) //nolint:goerr113 } - c.log.Debugf("Channel binding successful: %s %d", b.addr, b.number) + c.log.Debugf("Channel binding successful: %s %d", bound.addr, bound.number) // Success. return nil @@ -451,5 +462,6 @@ func (c *UDPConn) sendChannelData(data []byte, chNum uint16) (int, error) { if err != nil { return 0, err } + return len(data), nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/ipnet/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/ipnet/util.go index c3a5b32f3..9753ef35c 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/ipnet/util.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/ipnet/util.go @@ -11,7 +11,7 @@ import ( var errFailedToCastAddr = errors.New("failed to cast net.Addr to *net.UDPAddr or *net.TCPAddr") -// AddrIPPort extracts the IP and Port from a net.Addr +// AddrIPPort extracts the IP and Port from a net.Addr. func AddrIPPort(a net.Addr) (net.IP, int, error) { aUDP, ok := a.(*net.UDPAddr) if ok { @@ -27,7 +27,7 @@ func AddrIPPort(a net.Addr) (net.IP, int, error) { } // AddrEqual asserts that two net.Addrs are equal -// Currently only supports UDP but will be extended in the future to support others +// Currently only supports UDP but will be extended in the future to support others. func AddrEqual(a, b net.Addr) bool { aUDP, ok := a.(*net.UDPAddr) if !ok { @@ -51,5 +51,6 @@ func FingerprintAddr(addr net.Addr) string { case *net.TCPAddr: // Do we really need this case? return a.IP.String() } + return "" // Should never happen } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/addr.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/addr.go index 7d4db8bf7..38b3027fc 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/addr.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/addr.go @@ -28,6 +28,7 @@ func (a Addr) Equal(b Addr) bool { if a.Port != b.Port { return false } + return a.IP.Equal(b.IP) } @@ -64,5 +65,6 @@ func (t FiveTuple) Equal(b FiveTuple) bool { if !t.Server.Equal(b.Server) { return false } + return true } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chandata.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chandata.go index df9371193..4c9bb2620 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chandata.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chandata.go @@ -12,7 +12,7 @@ import ( // ChannelData represents The ChannelData Message. // -// See RFC 5766 Section 11.4 +// See RFC 5766 Section 11.4. type ChannelData struct { Data []byte // Can be sub slice of Raw Length int // Ignored while encoding, len(Data) is used @@ -20,21 +20,22 @@ type ChannelData struct { Raw []byte } -// Equal returns true if b == c. -func (c *ChannelData) Equal(b *ChannelData) bool { - if c == nil && b == nil { +// Equal returns true if compareTo == c. +func (c *ChannelData) Equal(compareTo *ChannelData) bool { + if c == nil && compareTo == nil { return true } - if c == nil || b == nil { + if c == nil || compareTo == nil { return false } - if c.Number != b.Number { + if c.Number != compareTo.Number { return false } - if len(c.Data) != len(b.Data) { + if len(c.Data) != len(compareTo.Data) { return false } - return bytes.Equal(c.Data, b.Data) + + return bytes.Equal(c.Data, compareTo.Data) } // Grow ensures that internal buffer will fit v more bytes and @@ -76,6 +77,7 @@ func nearestPaddedValueLength(l int) int { if n < l { n += padding } + return n } @@ -90,7 +92,7 @@ func (c *ChannelData) WriteHeader() { _ = c.Raw[:channelDataHeaderSize] binary.BigEndian.PutUint16(c.Raw[:channelDataNumberSize], uint16(c.Number)) binary.BigEndian.PutUint16(c.Raw[channelDataNumberSize:channelDataHeaderSize], - uint16(len(c.Data)), + uint16(len(c.Data)), // nolint:gosec // G115 ) } @@ -118,6 +120,7 @@ func (c *ChannelData) Decode() error { if int(l) > len(buf[channelDataHeaderSize:]) { return ErrBadChannelDataLength } + return nil } @@ -139,5 +142,6 @@ func IsChannelData(buf []byte) bool { // Quick check for channel number. num := binary.BigEndian.Uint16(buf[0:channelNumberSize]) + return isChannelNumberValid(num) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chann.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chann.go index 3aeb59f38..da017bf65 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chann.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/chann.go @@ -15,7 +15,7 @@ import ( // // The CHANNEL-NUMBER attribute contains the number of the channel. // -// RFC 5766 Section 14.1 +// RFC 5766 Section 14.1. type ChannelNumber uint16 // Encoded as uint16 func (n ChannelNumber) String() string { return strconv.Itoa(int(n)) } @@ -29,6 +29,7 @@ func (n ChannelNumber) AddTo(m *stun.Message) error { binary.BigEndian.PutUint16(v[:2], uint16(n)) // v[2:4] are zeroes (RFFU = 0) m.Add(stun.AttrChannelNumber, v) + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go index 568deff15..d95d9b045 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/connection_id.go @@ -14,7 +14,7 @@ import ( // The CONNECTION-ID attribute uniquely identifies a peer data // connection. It is a 32-bit unsigned integral value. // -// RFC 6062 Section 6.2.1 +// RFC 6062 Section 6.2.1. type ConnectionID uint32 const connectionIDSize = 4 // uint32: 4 bytes, 32 bits @@ -24,6 +24,7 @@ func (c ConnectionID) AddTo(m *stun.Message) error { v := make([]byte, lifetimeSize) binary.BigEndian.PutUint32(v, uint32(c)) m.Add(stun.AttrConnectionID, v) + return nil } @@ -38,5 +39,6 @@ func (c *ConnectionID) GetFrom(m *stun.Message) error { } _ = v[connectionIDSize-1] // Asserting length *(*uint32)(c) = binary.BigEndian.Uint32(v) + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/data.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/data.go index dcba23b44..ea2f0c868 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/data.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/data.go @@ -13,12 +13,13 @@ import "github.com/pion/stun/v3" // the UDP header if the data was been sent directly between the client // and the peer). // -// RFC 5766 Section 14.4 +// RFC 5766 Section 14.4. type Data []byte // AddTo adds DATA to message. func (d Data) AddTo(m *stun.Message) error { m.Add(stun.AttrData, d) + return nil } @@ -29,5 +30,6 @@ func (d *Data) GetFrom(m *stun.Message) error { return err } *d = v + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go index e4be0af67..d46ae8f14 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/dontfrag.go @@ -8,7 +8,7 @@ import ( ) // DontFragmentAttr is a deprecated alias for DontFragment -// Deprecated: Please use DontFragment +// Deprecated: Please use DontFragment. type DontFragmentAttr = DontFragment // DontFragment represents DONT-FRAGMENT attribute. @@ -18,7 +18,7 @@ type DontFragmentAttr = DontFragment // application data onward to the peer. This attribute has no value // part and thus the attribute length field is 0. // -// RFC 5766 Section 14.8 +// RFC 5766 Section 14.8. type DontFragment struct{} const dontFragmentSize = 0 @@ -26,6 +26,7 @@ const dontFragmentSize = 0 // AddTo adds DONT-FRAGMENT attribute to message. func (DontFragment) AddTo(m *stun.Message) error { m.Add(stun.AttrDontFragment, nil) + return nil } @@ -35,11 +36,13 @@ func (d *DontFragment) GetFrom(m *stun.Message) error { if err != nil { return err } + return stun.CheckSize(stun.AttrDontFragment, len(v), dontFragmentSize) } // IsSet returns true if DONT-FRAGMENT attribute is set. func (DontFragment) IsSet(m *stun.Message) bool { _, err := m.Get(stun.AttrDontFragment) + return err == nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/evenport.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/evenport.go index 31468c757..6989ab894 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/evenport.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/evenport.go @@ -11,7 +11,7 @@ import "github.com/pion/stun/v3" // relayed transport address be even, and (optionally) that the server // reserve the next-higher port number. // -// RFC 5766 Section 14.6 +// RFC 5766 Section 14.6. type EvenPort struct { // ReservePort means that the server is requested to reserve // the next-higher port number (on the same IP address) @@ -23,6 +23,7 @@ func (p EvenPort) String() string { if p.ReservePort { return "reserve: true" } + return "reserve: false" } @@ -39,6 +40,7 @@ func (p EvenPort) AddTo(m *stun.Message) error { v[0] = firstBitSet } m.Add(stun.AttrEvenPort, v) + return nil } @@ -54,5 +56,6 @@ func (p *EvenPort) GetFrom(m *stun.Message) error { if v[0]&firstBitSet > 0 { p.ReservePort = true } + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go index 6f55c7a97..37b86f81a 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/lifetime.go @@ -12,7 +12,7 @@ import ( // DefaultLifetime in RFC 5766 is 10 minutes. // -// RFC 5766 Section 2.2 +// RFC 5766 Section 2.2. const DefaultLifetime = time.Minute * 10 // Lifetime represents LIFETIME attribute. @@ -23,12 +23,12 @@ const DefaultLifetime = time.Minute * 10 // unsigned integral value representing the number of seconds remaining // until expiration. // -// RFC 5766 Section 14.2 +// RFC 5766 Section 14.2. type Lifetime struct { time.Duration } -// Seconds in uint32 +// Seconds in uint32. const lifetimeSize = 4 // 4 bytes, 32 bits // AddTo adds LIFETIME to message. @@ -36,6 +36,7 @@ func (l Lifetime) AddTo(m *stun.Message) error { v := make([]byte, lifetimeSize) binary.BigEndian.PutUint32(v, uint32(l.Seconds())) m.Add(stun.AttrLifetime, v) + return nil } @@ -51,5 +52,6 @@ func (l *Lifetime) GetFrom(m *stun.Message) error { _ = v[lifetimeSize-1] // Asserting length seconds := binary.BigEndian.Uint32(v) l.Duration = time.Second * time.Duration(seconds) + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go index f4d9de7b6..a919bd15a 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/peeraddr.go @@ -15,7 +15,7 @@ import ( // seen from the TURN server. (For example, the peer's server-reflexive // transport address if the peer is behind a NAT.) // -// RFC 5766 Section 14.3 +// RFC 5766 Section 14.3. type PeerAddress struct { IP net.IP Port int @@ -41,5 +41,5 @@ func (a *PeerAddress) GetFrom(m *stun.Message) error { // seen from the TURN server. (For example, the peer's server-reflexive // transport address if the peer is behind a NAT.) // -// RFC 5766 Section 14.3 +// RFC 5766 Section 14.3. type XORPeerAddress = PeerAddress diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go index c5fb2686c..1d22ade9e 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/relayedaddr.go @@ -14,7 +14,7 @@ import ( // It specifies the address and port that the server allocated to the // client. It is encoded in the same way as XOR-MAPPED-ADDRESS. // -// RFC 5766 Section 14.5 +// RFC 5766 Section 14.5. type RelayedAddress struct { IP net.IP Port int @@ -39,5 +39,5 @@ func (a *RelayedAddress) GetFrom(m *stun.Message) error { // It specifies the address and port that the server allocated to the // client. It is encoded in the same way as XOR-MAPPED-ADDRESS. // -// RFC 5766 Section 14.5 +// RFC 5766 Section 14.5. type XORRelayedAddress = RelayedAddress diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go index 01016a987..d00c302fc 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqfamily.go @@ -32,6 +32,7 @@ func (f *RequestedAddressFamily) GetFrom(m *stun.Message) error { default: return errInvalidRequestedFamilyValue } + return nil } @@ -54,6 +55,7 @@ func (f RequestedAddressFamily) AddTo(m *stun.Message) error { // The RFFU field MUST be set to zero on transmission and MUST be // ignored on reception. It is reserved for future uses. m.Add(stun.AttrRequestedAddressFamily, v) + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go index 111dcd69b..b907e3846 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/reqtrans.go @@ -36,7 +36,7 @@ func (p Protocol) String() string { // protocol for the allocated transport address. RFC 5766 only allows the use of // code point 17 (User Datagram Protocol). // -// RFC 5766 Section 14.7 +// RFC 5766 Section 14.7. type RequestedTransport struct { Protocol Protocol } @@ -55,6 +55,7 @@ func (t RequestedTransport) AddTo(m *stun.Message) error { // The RFFU field MUST be set to zero on transmission and MUST be // ignored on reception. It is reserved for future uses. m.Add(stun.AttrRequestedTransport, v) + return nil } @@ -68,5 +69,6 @@ func (t *RequestedTransport) GetFrom(m *stun.Message) error { return err } t.Protocol = Protocol(v[0]) + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go index 9c816485d..aed38861a 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/proto/rsrvtoken.go @@ -14,7 +14,7 @@ import "github.com/pion/stun/v3" // attribute in a subsequent Allocate request to request the server use // that relayed transport address for the allocation. // -// RFC 5766 Section 14.9 +// RFC 5766 Section 14.9. type ReservationToken []byte const reservationTokenSize = 8 // 8 bytes @@ -25,6 +25,7 @@ func (t ReservationToken) AddTo(m *stun.Message) error { return err } m.Add(stun.AttrReservationToken, t) + return nil } @@ -38,5 +39,6 @@ func (t *ReservationToken) GetFrom(m *stun.Message) error { return err } *t = v + return nil } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/nonce.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/nonce.go index b3f3131e8..22e466f98 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/nonce.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/nonce.go @@ -19,7 +19,7 @@ const ( nonceKeyLength = 64 ) -// NewNonceHash creates a NonceHash +// NewNonceHash creates a NonceHash. func NewNonceHash() (*NonceHash, error) { key := make([]byte, nonceKeyLength) if _, err := rand.Read(key); err != nil { @@ -29,15 +29,15 @@ func NewNonceHash() (*NonceHash, error) { return &NonceHash{key}, nil } -// NonceHash is used to create and verify nonces +// NonceHash is used to create and verify nonces. type NonceHash struct { key []byte } -// Generate a nonce +// Generate a nonce. func (n *NonceHash) Generate() (string, error) { nonce := make([]byte, 8, nonceLength) - binary.BigEndian.PutUint64(nonce, uint64(time.Now().UnixMilli())) + binary.BigEndian.PutUint64(nonce, uint64(time.Now().UnixMilli())) // nolint:gosec // G115 hash := hmac.New(sha256.New, n.key) if _, err := hash.Write(nonce[:8]); err != nil { @@ -48,14 +48,14 @@ func (n *NonceHash) Generate() (string, error) { return hex.EncodeToString(nonce), nil } -// Validate checks that nonce is signed and is not expired +// Validate checks that nonce is signed and is not expired. func (n *NonceHash) Validate(nonce string) error { b, err := hex.DecodeString(nonce) if err != nil || len(b) != nonceLength { return fmt.Errorf("%w: %v", errInvalidNonce, err) //nolint:errorlint } - if ts := time.UnixMilli(int64(binary.BigEndian.Uint64(b))); time.Since(ts) > nonceLifetime { + if ts := time.UnixMilli(int64(binary.BigEndian.Uint64(b))); time.Since(ts) > nonceLifetime { // nolint:gosec // G115 return errInvalidNonce } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/server.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/server.go index 253492e92..4f5c31486 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/server.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/server.go @@ -15,7 +15,7 @@ import ( "github.com/pion/turn/v4/internal/proto" ) -// Request contains all the state needed to process a single incoming datagram +// Request contains all the state needed to process a single incoming datagram. type Request struct { // Current Request State Conn net.PacketConn @@ -33,7 +33,7 @@ type Request struct { ChannelBindTimeout time.Duration } -// HandleRequest processes the give Request +// HandleRequest processes the give Request. func HandleRequest(r Request) error { r.Log.Debugf("Received %d bytes of udp from %s on %s", len(r.Buff), r.SrcAddr, r.Conn.LocalAddr()) @@ -44,42 +44,62 @@ func HandleRequest(r Request) error { return handleTURNPacket(r) } -func handleDataPacket(r Request) error { - r.Log.Debugf("Received DataPacket from %s", r.SrcAddr.String()) - c := proto.ChannelData{Raw: r.Buff} +func handleDataPacket(req Request) error { + req.Log.Debugf("Received DataPacket from %s", req.SrcAddr.String()) + c := proto.ChannelData{Raw: req.Buff} if err := c.Decode(); err != nil { return fmt.Errorf("%w: %v", errFailedToCreateChannelData, err) //nolint:errorlint } - err := handleChannelData(r, &c) + err := handleChannelData(req, &c) if err != nil { - err = fmt.Errorf("%w from %v: %v", errUnableToHandleChannelData, r.SrcAddr, err) //nolint:errorlint + err = fmt.Errorf("%w from %v: %v", errUnableToHandleChannelData, req.SrcAddr, err) //nolint:errorlint } return err } -func handleTURNPacket(r Request) error { - r.Log.Debug("Handling TURN packet") - m := &stun.Message{Raw: append([]byte{}, r.Buff...)} - if err := m.Decode(); err != nil { - return fmt.Errorf("%w: %v", errFailedToCreateSTUNPacket, err) //nolint:errorlint +func handleTURNPacket(req Request) error { + req.Log.Debug("Handling TURN packet") + stunMsg := &stun.Message{Raw: append([]byte{}, req.Buff...)} + if err := stunMsg.Decode(); err != nil { + // nolint:errorlint + return fmt.Errorf("%w: %v", errFailedToCreateSTUNPacket, err) } - h, err := getMessageHandler(m.Type.Class, m.Type.Method) + handler, err := getMessageHandler(stunMsg.Type.Class, stunMsg.Type.Method) if err != nil { - return fmt.Errorf("%w %v-%v from %v: %v", errUnhandledSTUNPacket, m.Type.Method, m.Type.Class, r.SrcAddr, err) //nolint:errorlint + // nolint:errorlint + return fmt.Errorf( + "%w %v-%v from %v: %v", + errUnhandledSTUNPacket, + stunMsg.Type.Method, + stunMsg.Type.Class, + req.SrcAddr, + err, + ) } - err = h(r, m) + err = handler(req, stunMsg) if err != nil { - return fmt.Errorf("%w %v-%v from %v: %v", errFailedToHandle, m.Type.Method, m.Type.Class, r.SrcAddr, err) //nolint:errorlint + // nolint:errorlint + return fmt.Errorf( + "%w %v-%v from %v: %v", + errFailedToHandle, + stunMsg.Type.Method, + stunMsg.Type.Class, + req.SrcAddr, + err, + ) } return nil } -func getMessageHandler(class stun.MessageClass, method stun.Method) (func(r Request, m *stun.Message) error, error) { +func getMessageHandler(class stun.MessageClass, method stun.Method) ( // nolint:cyclop + func(req Request, stunMsg *stun.Message) error, + error, +) { switch class { case stun.ClassIndication: switch method { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/stun.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/stun.go index 393bb99d8..1880bf177 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/stun.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/stun.go @@ -8,18 +8,18 @@ import ( "github.com/pion/turn/v4/internal/ipnet" ) -func handleBindingRequest(r Request, m *stun.Message) error { - r.Log.Debugf("Received BindingRequest from %s", r.SrcAddr) +func handleBindingRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received BindingRequest from %s", req.SrcAddr) - ip, port, err := ipnet.AddrIPPort(r.SrcAddr) + ip, port, err := ipnet.AddrIPPort(req.SrcAddr) if err != nil { return err } - attrs := buildMsg(m.TransactionID, stun.BindingSuccess, &stun.XORMappedAddress{ + attrs := buildMsg(stunMsg.TransactionID, stun.BindingSuccess, &stun.XORMappedAddress{ IP: ip, Port: port, }, stun.Fingerprint) - return buildAndSend(r.Conn, r.SrcAddr, attrs...) + return buildAndSend(req.Conn, req.SrcAddr, attrs...) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/turn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/turn.go index 46e45ecbe..d20c5dc56 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/turn.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/turn.go @@ -17,42 +17,61 @@ import ( const runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" // See: https://tools.ietf.org/html/rfc5766#section-6.2 -func handleAllocateRequest(r Request, m *stun.Message) error { - r.Log.Debugf("Received AllocateRequest from %s", r.SrcAddr) +// . +func handleAllocateRequest(req Request, stunMsg *stun.Message) error { //nolint:cyclop + req.Log.Debugf("Received AllocateRequest from %s", req.SrcAddr) // 1. The server MUST require that the request be authenticated. This // authentication MUST be done using the long-term credential // mechanism of [https://tools.ietf.org/html/rfc5389#section-10.2.2] // unless the client and server agree to use another mechanism through // some procedure outside the scope of this document. - messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodAllocate) + messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodAllocate) if !hasAuth { return err } fiveTuple := &allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, } requestedPort := 0 reservationToken := "" - badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}) - insufficientCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity}) + badRequestMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + ) + insufficientCapacityMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity}, + ) // 2. The server checks if the 5-tuple is currently in use by an // existing allocation. If yes, the server rejects the request with // a 437 (Allocation Mismatch) error. - if alloc := r.AllocationManager.GetAllocation(fiveTuple); alloc != nil { + if alloc := req.AllocationManager.GetAllocation(fiveTuple); alloc != nil { id, attrs := alloc.GetResponseCache() - if id != m.TransactionID { - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch}) - return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...) + if id != stunMsg.TransactionID { + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...) } // A retry allocation - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(attrs, messageIntegrity)...) - return buildAndSend(r.Conn, r.SrcAddr, msg...) + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), + append(attrs, messageIntegrity)..., + ) + + return buildAndSend(req.Conn, req.SrcAddr, msg...) } // 3. The server checks if the request contains a REQUESTED-TRANSPORT @@ -62,11 +81,16 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // specifies a protocol other that UDP/TCP, the server rejects the // request with a 442 (Unsupported Transport Protocol) error. var requestedTransport proto.RequestedTransport - if err = requestedTransport.GetFrom(m); err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err = requestedTransport.GetFrom(stunMsg); err != nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } else if requestedTransport.Protocol != proto.ProtoUDP && requestedTransport.Protocol != proto.ProtoTCP { - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnsupportedTransProto}) - return buildAndSendErr(r.Conn, r.SrcAddr, errUnsupportedTransportProtocol, msg...) + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeUnsupportedTransProto}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errUnsupportedTransportProtocol, msg...) } // 4. The request may contain a DONT-FRAGMENT attribute. If it does, @@ -74,9 +98,15 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // bit set to 1 (see Section 12), then the server treats the DONT- // FRAGMENT attribute in the Allocate request as an unknown // comprehension-required attribute. - if m.Contains(stun.AttrDontFragment) { - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute}, &stun.UnknownAttributes{stun.AttrDontFragment}) - return buildAndSendErr(r.Conn, r.SrcAddr, errNoDontFragmentSupport, msg...) + if stunMsg.Contains(stun.AttrDontFragment) { + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute}, + &stun.UnknownAttributes{stun.AttrDontFragment}, + ) + + return buildAndSendErr(req.Conn, req.SrcAddr, errNoDontFragmentSupport, msg...) } // 5. The server checks if the request contains a RESERVATION-TOKEN @@ -88,10 +118,10 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // the token is not valid for some reason, the server rejects the // request with a 508 (Insufficient Capacity) error. var reservationTokenAttr proto.ReservationToken - if err = reservationTokenAttr.GetFrom(m); err == nil { + if err = reservationTokenAttr.GetFrom(stunMsg); err == nil { var evenPort proto.EvenPort - if err = evenPort.GetFrom(m); err == nil { - return buildAndSendErr(r.Conn, r.SrcAddr, errRequestWithReservationTokenAndEvenPort, badRequestMsg...) + if err = evenPort.GetFrom(stunMsg); err == nil { + return buildAndSendErr(req.Conn, req.SrcAddr, errRequestWithReservationTokenAndEvenPort, badRequestMsg...) } } @@ -102,11 +132,11 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // server rejects the request with a 508 (Insufficient Capacity) // error. var evenPort proto.EvenPort - if err = evenPort.GetFrom(m); err == nil { + if err = evenPort.GetFrom(stunMsg); err == nil { var randomPort int - randomPort, err = r.AllocationManager.GetRandomEvenPort() + randomPort, err = req.AllocationManager.GetRandomEvenPort() if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...) + return buildAndSendErr(req.Conn, req.SrcAddr, err, insufficientCapacityMsg...) } requestedPort = randomPort reservationToken, err = randutil.GenerateCryptoRandomString(8, runesAlpha) @@ -126,14 +156,14 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // with a 300 (Try Alternate) error if it wishes to redirect the // client to a different server. The use of this error code and // attribute follow the specification in [RFC5389]. - lifetimeDuration := allocationLifeTime(m) - a, err := r.AllocationManager.CreateAllocation( + lifetimeDuration := allocationLifeTime(stunMsg) + alloc, err := req.AllocationManager.CreateAllocation( fiveTuple, - r.Conn, + req.Conn, requestedPort, lifetimeDuration) if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...) + return buildAndSendErr(req.Conn, req.SrcAddr, err, insufficientCapacityMsg...) } // Once the allocation is created, the server replies with a success @@ -148,14 +178,14 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // * An XOR-MAPPED-ADDRESS attribute containing the client's IP address // and port (from the 5-tuple). - srcIP, srcPort, err := ipnet.AddrIPPort(r.SrcAddr) + srcIP, srcPort, err := ipnet.AddrIPPort(req.SrcAddr) if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - relayIP, relayPort, err := ipnet.AddrIPPort(a.RelayAddr) + relayIP, relayPort, err := ipnet.AddrIPPort(alloc.RelayAddr) if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } responseAttrs := []stun.Setter{ @@ -173,90 +203,105 @@ func handleAllocateRequest(r Request, m *stun.Message) error { } if reservationToken != "" { - r.AllocationManager.CreateReservation(reservationToken, relayPort) + req.AllocationManager.CreateReservation(reservationToken, relayPort) responseAttrs = append(responseAttrs, proto.ReservationToken([]byte(reservationToken))) } - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(responseAttrs, messageIntegrity)...) - a.SetResponseCache(m.TransactionID, responseAttrs) - return buildAndSend(r.Conn, r.SrcAddr, msg...) + msg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), + append(responseAttrs, messageIntegrity)..., + ) + alloc.SetResponseCache(stunMsg.TransactionID, responseAttrs) + + return buildAndSend(req.Conn, req.SrcAddr, msg...) } -func handleRefreshRequest(r Request, m *stun.Message) error { - r.Log.Debugf("Received RefreshRequest from %s", r.SrcAddr) +func handleRefreshRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received RefreshRequest from %s", req.SrcAddr) - messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodRefresh) + messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodRefresh) if !hasAuth { return err } - lifetimeDuration := allocationLifeTime(m) + lifetimeDuration := allocationLifeTime(stunMsg) fiveTuple := &allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, } if lifetimeDuration != 0 { - a := r.AllocationManager.GetAllocation(fiveTuple) + a := req.AllocationManager.GetAllocation(fiveTuple) if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr()) + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } a.Refresh(lifetimeDuration) } else { - r.AllocationManager.DeleteAllocation(fiveTuple) + req.AllocationManager.DeleteAllocation(fiveTuple) } - return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodRefresh, stun.ClassSuccessResponse), []stun.Setter{ - &proto.Lifetime{ - Duration: lifetimeDuration, - }, - messageIntegrity, - }...)...) + return buildAndSend( + req.Conn, + req.SrcAddr, + buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodRefresh, stun.ClassSuccessResponse), + []stun.Setter{ + &proto.Lifetime{ + Duration: lifetimeDuration, + }, + messageIntegrity, + }..., + )..., + ) } -func handleCreatePermissionRequest(r Request, m *stun.Message) error { - r.Log.Debugf("Received CreatePermission from %s", r.SrcAddr) +func handleCreatePermissionRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received CreatePermission from %s", req.SrcAddr) - a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), + alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, }) - if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr()) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } - messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodCreatePermission) + messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodCreatePermission) if !hasAuth { return err } addCount := 0 - if err := m.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error { + if err := stunMsg.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error { var peerAddress proto.PeerAddress if err := peerAddress.GetFrom(m); err != nil { return err } - if err := r.AllocationManager.GrantPermission(r.SrcAddr, peerAddress.IP); err != nil { - r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr, peerAddress.IP) + if err := req.AllocationManager.GrantPermission(req.SrcAddr, peerAddress.IP); err != nil { + req.Log.Infof("permission denied for client %s to peer %s", req.SrcAddr, peerAddress.IP) + return err } - r.Log.Debugf("Adding permission for %s", fmt.Sprintf("%s:%d", + req.Log.Debugf("Adding permission for %s", fmt.Sprintf("%s:%d", peerAddress.IP, peerAddress.Port)) - a.AddPermission(allocation.NewPermission( + alloc.AddPermission(allocation.NewPermission( &net.UDPAddr{ IP: peerAddress.IP, Port: peerAddress.Port, }, - r.Log, + req.Log, )) addCount++ + return nil }); err != nil { addCount = 0 @@ -267,115 +312,131 @@ func handleCreatePermissionRequest(r Request, m *stun.Message) error { respClass = stun.ClassErrorResponse } - return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodCreatePermission, respClass), []stun.Setter{messageIntegrity}...)...) + return buildAndSend( + req.Conn, + req.SrcAddr, + buildMsg(stunMsg.TransactionID, stun.NewType(stun.MethodCreatePermission, respClass), + []stun.Setter{messageIntegrity}...)..., + ) } -func handleSendIndication(r Request, m *stun.Message) error { - r.Log.Debugf("Received SendIndication from %s", r.SrcAddr) - a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), +func handleSendIndication(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received SendIndication from %s", req.SrcAddr) + alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, }) - if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr()) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } dataAttr := proto.Data{} - if err := dataAttr.GetFrom(m); err != nil { + if err := dataAttr.GetFrom(stunMsg); err != nil { return err } peerAddress := proto.PeerAddress{} - if err := peerAddress.GetFrom(m); err != nil { + if err := peerAddress.GetFrom(stunMsg); err != nil { return err } msgDst := &net.UDPAddr{IP: peerAddress.IP, Port: peerAddress.Port} - if perm := a.GetPermission(msgDst); perm == nil { + if perm := alloc.GetPermission(msgDst); perm == nil { return fmt.Errorf("%w: %v", errNoPermission, msgDst) } - l, err := a.RelaySocket.WriteTo(dataAttr, msgDst) + l, err := alloc.RelaySocket.WriteTo(dataAttr, msgDst) if l != len(dataAttr) { return fmt.Errorf("%w %d != %d (expected) err: %v", errShortWrite, l, len(dataAttr), err) //nolint:errorlint } + return err } -func handleChannelBindRequest(r Request, m *stun.Message) error { - r.Log.Debugf("Received ChannelBindRequest from %s", r.SrcAddr) +func handleChannelBindRequest(req Request, stunMsg *stun.Message) error { + req.Log.Debugf("Received ChannelBindRequest from %s", req.SrcAddr) - a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), + alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, }) - if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr()) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } - badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}) + badRequestMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + ) - messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodChannelBind) + messageIntegrity, hasAuth, err := authenticateRequest(req, stunMsg, stun.MethodChannelBind) if !hasAuth { return err } var channel proto.ChannelNumber - if err = channel.GetFrom(m); err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err = channel.GetFrom(stunMsg); err != nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } peerAddr := proto.PeerAddress{} - if err = peerAddr.GetFrom(m); err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err = peerAddr.GetFrom(stunMsg); err != nil { + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - if err = r.AllocationManager.GrantPermission(r.SrcAddr, peerAddr.IP); err != nil { - r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr, peerAddr.IP) + if err = req.AllocationManager.GrantPermission(req.SrcAddr, peerAddr.IP); err != nil { + req.Log.Infof("permission denied for client %s to peer %s", req.SrcAddr, peerAddr.IP) - unauthorizedRequestMsg := buildMsg(m.TransactionID, + unauthorizedRequestMsg := buildMsg(stunMsg.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnauthorized}) - return buildAndSendErr(r.Conn, r.SrcAddr, err, unauthorizedRequestMsg...) + + return buildAndSendErr(req.Conn, req.SrcAddr, err, unauthorizedRequestMsg...) } - r.Log.Debugf("Binding channel %d to %s", channel, peerAddr) - err = a.AddChannelBind(allocation.NewChannelBind( + req.Log.Debugf("Binding channel %d to %s", channel, peerAddr) + err = alloc.AddChannelBind(allocation.NewChannelBind( channel, &net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port}, - r.Log, - ), r.ChannelBindTimeout) + req.Log, + ), req.ChannelBindTimeout) if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + return buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse), []stun.Setter{messageIntegrity}...)...) + return buildAndSend( + req.Conn, + req.SrcAddr, + buildMsg(stunMsg.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse), + []stun.Setter{messageIntegrity}...)..., + ) } -func handleChannelData(r Request, c *proto.ChannelData) error { - r.Log.Debugf("Received ChannelData from %s", r.SrcAddr) +func handleChannelData(req Request, channelData *proto.ChannelData) error { + req.Log.Debugf("Received ChannelData from %s", req.SrcAddr) - a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{ - SrcAddr: r.SrcAddr, - DstAddr: r.Conn.LocalAddr(), + alloc := req.AllocationManager.GetAllocation(&allocation.FiveTuple{ + SrcAddr: req.SrcAddr, + DstAddr: req.Conn.LocalAddr(), Protocol: allocation.UDP, }) - if a == nil { - return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr()) + if alloc == nil { + return fmt.Errorf("%w %v:%v", errNoAllocationFound, req.SrcAddr, req.Conn.LocalAddr()) } - channel := a.GetChannelByNumber(c.Number) + channel := alloc.GetChannelByNumber(channelData.Number) if channel == nil { - return fmt.Errorf("%w %x", errNoSuchChannelBind, uint16(c.Number)) + return fmt.Errorf("%w %x", errNoSuchChannelBind, uint16(channelData.Number)) } - l, err := a.RelaySocket.WriteTo(c.Data, channel.Peer) + l, err := alloc.RelaySocket.WriteTo(channelData.Data, channel.Peer) if err != nil { return fmt.Errorf("%w: %s", errFailedWriteSocket, err.Error()) - } else if l != len(c.Data) { - return fmt.Errorf("%w %d != %d (expected)", errShortWrite, l, len(c.Data)) + } else if l != len(channelData.Data) { + return fmt.Errorf("%w %d != %d (expected)", errShortWrite, l, len(channelData.Data)) } return nil diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/util.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/util.go index 7c01d3297..a16bf439f 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/util.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/internal/server/util.go @@ -14,7 +14,8 @@ import ( ) const ( - maximumAllocationLifetime = time.Hour // See: https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 seconds recommendation + // See: https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 seconds recommendation. + maximumAllocationLifetime = time.Hour ) func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error { @@ -30,71 +31,90 @@ func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error return err } -// Send a STUN packet and return the original error to the caller +// Send a STUN packet and return the original error to the caller. func buildAndSendErr(conn net.PacketConn, dst net.Addr, err error, attrs ...stun.Setter) error { if sendErr := buildAndSend(conn, dst, attrs...); sendErr != nil { err = fmt.Errorf("%w %v %v", errFailedToSendError, sendErr, err) //nolint:errorlint } + return err } -func buildMsg(transactionID [stun.TransactionIDSize]byte, msgType stun.MessageType, additional ...stun.Setter) []stun.Setter { +func buildMsg( + transactionID [stun.TransactionIDSize]byte, + msgType stun.MessageType, + additional ...stun.Setter, +) []stun.Setter { return append([]stun.Setter{&stun.Message{TransactionID: transactionID}, msgType}, additional...) } -func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method) (stun.MessageIntegrity, bool, error) { +func authenticateRequest(req Request, stunMsg *stun.Message, callingMethod stun.Method) ( + stun.MessageIntegrity, + bool, + error, +) { respondWithNonce := func(responseCode stun.ErrorCode) (stun.MessageIntegrity, bool, error) { - nonce, err := r.NonceHash.Generate() + nonce, err := req.NonceHash.Generate() if err != nil { return nil, false, err } - return nil, false, buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, + return nil, false, buildAndSend(req.Conn, req.SrcAddr, buildMsg(stunMsg.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: responseCode}, stun.NewNonce(nonce), - stun.NewRealm(r.Realm), + stun.NewRealm(req.Realm), )...) } - if !m.Contains(stun.AttrMessageIntegrity) { + if !stunMsg.Contains(stun.AttrMessageIntegrity) { return respondWithNonce(stun.CodeUnauthorized) } nonceAttr := &stun.Nonce{} usernameAttr := &stun.Username{} realmAttr := &stun.Realm{} - badRequestMsg := buildMsg(m.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}) + badRequestMsg := buildMsg( + stunMsg.TransactionID, + stun.NewType(callingMethod, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}, + ) // No Auth handler is set, server is running in STUN only mode - // Respond with 400 so clients don't retry - if r.AuthHandler == nil { - sendErr := buildAndSend(r.Conn, r.SrcAddr, badRequestMsg...) + // Respond with 400 so clients don't retry. + if req.AuthHandler == nil { + sendErr := buildAndSend(req.Conn, req.SrcAddr, badRequestMsg...) + return nil, false, sendErr } - if err := nonceAttr.GetFrom(m); err != nil { - return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err := nonceAttr.GetFrom(stunMsg); err != nil { + return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - // Assert Nonce is signed and is not expired - if err := r.NonceHash.Validate(nonceAttr.String()); err != nil { + // Assert Nonce is signed and is not expired. + if err := req.NonceHash.Validate(nonceAttr.String()); err != nil { return respondWithNonce(stun.CodeStaleNonce) } - if err := realmAttr.GetFrom(m); err != nil { - return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) - } else if err := usernameAttr.GetFrom(m); err != nil { - return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err := realmAttr.GetFrom(stunMsg); err != nil { + return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) + } else if err := usernameAttr.GetFrom(stunMsg); err != nil { + return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } - ourKey, ok := r.AuthHandler(usernameAttr.String(), realmAttr.String(), r.SrcAddr) + ourKey, ok := req.AuthHandler(usernameAttr.String(), realmAttr.String(), req.SrcAddr) if !ok { - return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), badRequestMsg...) + return nil, false, buildAndSendErr( + req.Conn, + req.SrcAddr, + fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), + badRequestMsg..., + ) } - if err := stun.MessageIntegrity(ourKey).Check(m); err != nil { - return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) + if err := stun.MessageIntegrity(ourKey).Check(stunMsg); err != nil { + return nil, false, buildAndSendErr(req.Conn, req.SrcAddr, err, badRequestMsg...) } return stun.MessageIntegrity(ourKey), true, nil diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/lt_cred.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/lt_cred.go index 42466c381..65576be28 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/lt_cred.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/lt_cred.go @@ -3,7 +3,7 @@ package turn -import ( //nolint:gci +import ( "crypto/hmac" "crypto/sha1" //nolint:gosec,gci "encoding/base64" @@ -15,20 +15,26 @@ import ( //nolint:gci "github.com/pion/logging" ) -// GenerateLongTermCredentials can be used to create credentials valid for [duration] time +// GenerateLongTermCredentials can be used to create credentials valid for [duration] time. func GenerateLongTermCredentials(sharedSecret string, duration time.Duration) (string, string, error) { t := time.Now().Add(duration).Unix() username := strconv.FormatInt(t, 10) password, err := longTermCredentials(username, sharedSecret) + return username, password, err } -// GenerateLongTermTURNRESTCredentials can be used to create credentials valid for [duration] time -func GenerateLongTermTURNRESTCredentials(sharedSecret string, user string, duration time.Duration) (string, string, error) { +// GenerateLongTermTURNRESTCredentials can be used to create credentials valid for [duration] time. +func GenerateLongTermTURNRESTCredentials(sharedSecret string, user string, duration time.Duration) ( + string, + string, + error, +) { t := time.Now().Add(duration).Unix() timestamp := strconv.FormatInt(t, 10) username := timestamp + ":" + user password, err := longTermCredentials(username, sharedSecret) + return username, password, err } @@ -39,31 +45,38 @@ func longTermCredentials(username string, sharedSecret string) (string, error) { return "", err // Not sure if this will ever happen } password := mac.Sum(nil) + return base64.StdEncoding.EncodeToString(password), nil } // NewLongTermAuthHandler returns a turn.AuthAuthHandler used with Long Term (or Time Windowed) Credentials. // See: https://datatracker.ietf.org/doc/html/rfc8489#section-9.2 -func NewLongTermAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHandler { - if l == nil { - l = logging.NewDefaultLoggerFactory().NewLogger("turn") +// . +func NewLongTermAuthHandler(sharedSecret string, logger logging.LeveledLogger) AuthHandler { + if logger == nil { + logger = logging.NewDefaultLoggerFactory().NewLogger("turn") } + return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { - l.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) + logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) t, err := strconv.Atoi(username) if err != nil { - l.Errorf("Invalid time-windowed username %q", username) + logger.Errorf("Invalid time-windowed username %q", username) + return nil, false } if int64(t) < time.Now().Unix() { - l.Errorf("Expired time-windowed username %q", username) + logger.Errorf("Expired time-windowed username %q", username) + return nil, false } password, err := longTermCredentials(username, sharedSecret) if err != nil { - l.Error(err.Error()) + logger.Error(err.Error()) + return nil, false } + return GenerateAuthKey(username, realm, password), true } } @@ -74,27 +87,32 @@ func NewLongTermAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHa // // The supported format of is timestamp:username, where username is an arbitrary user id and the // timestamp specifies the expiry of the credential. -func LongTermTURNRESTAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHandler { - if l == nil { - l = logging.NewDefaultLoggerFactory().NewLogger("turn") +func LongTermTURNRESTAuthHandler(sharedSecret string, logger logging.LeveledLogger) AuthHandler { + if logger == nil { + logger = logging.NewDefaultLoggerFactory().NewLogger("turn") } + return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { - l.Tracef("Authentication username=%q realm=%q srcAddr=%v\n", username, realm, srcAddr) + logger.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) timestamp := strings.Split(username, ":")[0] t, err := strconv.Atoi(timestamp) if err != nil { - l.Errorf("Invalid time-windowed username %q", username) + logger.Errorf("Invalid time-windowed username %q", username) + return nil, false } if int64(t) < time.Now().Unix() { - l.Errorf("Expired time-windowed username %q", username) + logger.Errorf("Expired time-windowed username %q", username) + return nil, false } password, err := longTermCredentials(username, sharedSecret) if err != nil { - l.Error(err.Error()) + logger.Error(err.Error()) + return nil, false } + return GenerateAuthKey(username, realm, password), true } } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_none.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_none.go index b0974010c..f60b10308 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_none.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_none.go @@ -12,7 +12,7 @@ import ( "github.com/pion/transport/v3/stdnet" ) -// RelayAddressGeneratorNone returns the listener with no modifications +// RelayAddressGeneratorNone returns the listener with no modifications. type RelayAddressGeneratorNone struct { // Address is passed to Listen/ListenPacket when creating the Relay Address string @@ -20,7 +20,7 @@ type RelayAddressGeneratorNone struct { Net transport.Net } -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. func (r *RelayAddressGeneratorNone) Validate() error { if r.Net == nil { var err error @@ -38,8 +38,13 @@ func (r *RelayAddressGeneratorNone) Validate() error { } } -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with -func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) { +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requestedPort int) ( + net.PacketConn, + net.Addr, + error, +) { conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort)) if err != nil { return nil, nil, err @@ -48,7 +53,8 @@ func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requested return conn, conn.LocalAddr(), nil } -// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with +// AllocateConn generates a new Conn to receive traffic on and the IP/Port +// to populate the allocation response with. func (r *RelayAddressGeneratorNone) AllocateConn(string, int) (net.Conn, net.Addr, error) { return nil, nil, errTODO } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_range.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_range.go index d87a57f9f..5b3c42953 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_range.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_range.go @@ -35,7 +35,7 @@ type RelayAddressGeneratorPortRange struct { Net transport.Net } -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. func (r *RelayAddressGeneratorPortRange) Validate() error { if r.Net == nil { var err error @@ -67,24 +67,30 @@ func (r *RelayAddressGeneratorPortRange) Validate() error { } } -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with -func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) { +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorPortRange) AllocatePacketConn( + network string, + requestedPort int, +) (net.PacketConn, net.Addr, error) { if requestedPort != 0 { conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, requestedPort)) if err != nil { return nil, nil, err } + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) if !ok { return nil, nil, errNilConn } relayAddr.IP = r.RelayAddress + return conn, relayAddr, nil } for try := 0; try < r.MaxRetries; try++ { - port := r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort))) + port := r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort))) // nolint:gosec // G115 false positive conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, port)) if err != nil { continue @@ -96,13 +102,15 @@ func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requ } relayAddr.IP = r.RelayAddress + return conn, relayAddr, nil } return nil, nil, errMaxRetriesExceeded } -// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with +// AllocateConn generates a new Conn to receive traffic on and the IP/Port +// to populate the allocation response with. func (r *RelayAddressGeneratorPortRange) AllocateConn(string, int) (net.Conn, net.Addr, error) { return nil, nil, errTODO } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_static.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_static.go index 39c687779..12ea5f254 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_static.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/relay_address_generator_static.go @@ -13,7 +13,7 @@ import ( ) // RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created. -// This can be used when you have a single static IP address that you want to use +// This can be used when you have a single static IP address that you want to use. type RelayAddressGeneratorStatic struct { // RelayAddress is the IP returned to the user when the relay is created RelayAddress net.IP @@ -24,7 +24,7 @@ type RelayAddressGeneratorStatic struct { Net transport.Net } -// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured. func (r *RelayAddressGeneratorStatic) Validate() error { if r.Net == nil { var err error @@ -44,8 +44,12 @@ func (r *RelayAddressGeneratorStatic) Validate() error { } } -// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with -func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) { +// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port +// to populate the allocation response with. +func (r *RelayAddressGeneratorStatic) AllocatePacketConn( + network string, + requestedPort int, +) (net.PacketConn, net.Addr, error) { conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort)) if err != nil { return nil, nil, err @@ -62,7 +66,8 @@ func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, request return conn, relayAddr, nil } -// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with +// AllocateConn generates a new Conn to receive traffic on and the IP/Port +// to populate the allocation response with. func (r *RelayAddressGeneratorStatic) AllocateConn(string, int) (net.Conn, net.Addr, error) { return nil, nil, errTODO } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server.go index 3b58938ff..5d03643db 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server.go @@ -20,7 +20,7 @@ const ( defaultInboundMTU = 1600 ) -// Server is an instance of the Pion TURN Server +// Server is an instance of the Pion TURN Server. type Server struct { log logging.LeveledLogger authHandler AuthHandler @@ -34,10 +34,8 @@ type Server struct { inboundMTU int } -// NewServer creates the Pion TURN server -// -//nolint:gocognit -func NewServer(config ServerConfig) (*Server, error) { +// NewServer creates the Pion TURN server. +func NewServer(config ServerConfig) (*Server, error) { //nolint:gocognit,cyclop if err := config.validate(); err != nil { return nil, err } @@ -57,7 +55,7 @@ func NewServer(config ServerConfig) (*Server, error) { return nil, err } - s := &Server{ + server := &Server{ log: loggerFactory.NewLogger("turn"), authHandler: config.AuthHandler, realm: config.Realm, @@ -68,53 +66,56 @@ func NewServer(config ServerConfig) (*Server, error) { inboundMTU: mtu, } - if s.channelBindTimeout == 0 { - s.channelBindTimeout = proto.DefaultLifetime + if server.channelBindTimeout == 0 { + server.channelBindTimeout = proto.DefaultLifetime } - for _, cfg := range s.packetConnConfigs { - am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) + for _, cfg := range server.packetConnConfigs { + am, err := server.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) if err != nil { return nil, fmt.Errorf("failed to create AllocationManager: %w", err) } go func(cfg PacketConnConfig, am *allocation.Manager) { - s.readLoop(cfg.PacketConn, am) + server.readLoop(cfg.PacketConn, am) if err := am.Close(); err != nil { - s.log.Errorf("Failed to close AllocationManager: %s", err) + server.log.Errorf("Failed to close AllocationManager: %s", err) } }(cfg, am) } - for _, cfg := range s.listenerConfigs { - am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) + for _, cfg := range server.listenerConfigs { + am, err := server.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) if err != nil { return nil, fmt.Errorf("failed to create AllocationManager: %w", err) } go func(cfg ListenerConfig, am *allocation.Manager) { - s.readListener(cfg.Listener, am) + server.readListener(cfg.Listener, am) if err := am.Close(); err != nil { - s.log.Errorf("Failed to close AllocationManager: %s", err) + server.log.Errorf("Failed to close AllocationManager: %s", err) } }(cfg, am) } - return s, nil + return server, nil } -// AllocationCount returns the number of active allocations. It can be used to drain the server before closing +// AllocationCount returns the number of active allocations. +// It can be used to drain the server before closing. func (s *Server) AllocationCount() int { allocs := 0 for _, am := range s.allocationManagers { allocs += am.AllocationCount() } + return allocs } -// Close stops the TURN Server. It cleans up any associated state and closes all connections it is managing +// Close stops the TURN Server. +// It cleans up any associated state and closes all connections it is managing. func (s *Server) Close() error { var errors []error @@ -147,6 +148,7 @@ func (s *Server) readListener(l net.Listener, am *allocation.Manager) { conn, err := l.Accept() if err != nil { s.log.Debugf("Failed to accept: %s", err) + return } @@ -179,7 +181,10 @@ func (n *nilAddressGenerator) AllocateConn(string, int) (net.Conn, net.Addr, err return nil, nil, errRelayAddressGeneratorNil } -func (s *Server) createAllocationManager(addrGenerator RelayAddressGenerator, handler PermissionHandler) (*allocation.Manager, error) { +func (s *Server) createAllocationManager( + addrGenerator RelayAddressGenerator, + handler PermissionHandler, +) (*allocation.Manager, error) { if handler == nil { handler = DefaultPermissionHandler } @@ -202,21 +207,23 @@ func (s *Server) createAllocationManager(addrGenerator RelayAddressGenerator, ha return am, err } -func (s *Server) readLoop(p net.PacketConn, allocationManager *allocation.Manager) { +func (s *Server) readLoop(conn net.PacketConn, allocationManager *allocation.Manager) { buf := make([]byte, s.inboundMTU) for { - n, addr, err := p.ReadFrom(buf) + n, addr, err := conn.ReadFrom(buf) switch { case err != nil: s.log.Debugf("Exit read loop on error: %s", err) + return case n >= s.inboundMTU: s.log.Debugf("Read bytes exceeded MTU, packet is possibly truncated") + continue } if err := server.HandleRequest(server.Request{ - Conn: p, + Conn: conn, SrcAddr: addr, Buff: buf[:n], Log: s.log, @@ -226,7 +233,7 @@ func (s *Server) readLoop(p net.PacketConn, allocationManager *allocation.Manage ChannelBindTimeout: s.channelBindTimeout, NonceHash: s.nonceHash, }); err != nil { - s.log.Errorf("Failed to handle datagram: %v", err) + s.log.Debugf("Failed to handle datagram: %v", err) } } } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server_config.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server_config.go index eaa7a2581..8f2761403 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server_config.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/server_config.go @@ -34,12 +34,13 @@ type RelayAddressGenerator interface { // of NATs that comply with [RFC4787], see https://tools.ietf.org/html/rfc5766#section-2.3. type PermissionHandler func(clientAddr net.Addr, peerIP net.IP) (ok bool) -// DefaultPermissionHandler is convince function that grants permission to all peers +// DefaultPermissionHandler is convince function that grants permission to all peers. func DefaultPermissionHandler(net.Addr, net.IP) (ok bool) { return true } -// PacketConnConfig is a single net.PacketConn to listen/write on. This will be used for UDP listeners +// PacketConnConfig is a single net.PacketConn to listen/write on. +// This will be used for UDP listeners. type PacketConnConfig struct { PacketConn net.PacketConn @@ -67,7 +68,8 @@ func (c *PacketConnConfig) validate() error { return nil } -// ListenerConfig is a single net.Listener to accept connections on. This will be used for TCP, TLS and DTLS listeners +// ListenerConfig is a single net.Listener to accept connections on. +// This will be used for TCP, TLS and DTLS listeners. type ListenerConfig struct { Listener net.Listener @@ -93,18 +95,20 @@ func (c *ListenerConfig) validate() error { return c.RelayAddressGenerator.Validate() } -// AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior +// AuthHandler is a callback used to handle incoming auth requests, +// allowing users to customize Pion TURN with custom behavior. type AuthHandler func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) -// GenerateAuthKey is a convenience function to easily generate keys in the format used by AuthHandler +// GenerateAuthKey is a convenience function to easily generate keys in the format used by AuthHandler. func GenerateAuthKey(username, realm, password string) []byte { // #nosec h := md5.New() - fmt.Fprint(h, strings.Join([]string{username, realm, password}, ":")) + fmt.Fprint(h, strings.Join([]string{username, realm, password}, ":")) // nolint: errcheck + return h.Sum(nil) } -// ServerConfig configures the Pion TURN Server +// ServerConfig configures the Pion TURN Server. type ServerConfig struct { // PacketConnConfigs and ListenerConfigs are a list of all the turn listeners // Each listener can have custom behavior around the creation of Relays @@ -117,7 +121,8 @@ type ServerConfig struct { // Realm sets the realm for this server Realm string - // AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior + // AuthHandler is a callback used to handle incoming auth requests, + // allowing users to customize Pion TURN with custom behavior AuthHandler AuthHandler // ChannelBindTimeout sets the lifetime of channel binding. Defaults to 10 minutes. diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/stun_conn.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/stun_conn.go index 57543544a..fd1f1ae6e 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/stun_conn.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/turn/v4/stun_conn.go @@ -20,7 +20,7 @@ var ( // STUNConn wraps a net.Conn and implements // net.PacketConn by being STUN aware and -// packetizing the stream +// packetizing the stream. type STUNConn struct { nextConn net.Conn buff []byte @@ -36,92 +36,93 @@ const ( ) // Given a buffer give the last offset of the TURN frame -// If the buffer isn't a valid STUN or ChannelData packet -// or the length doesn't match return false -func consumeSingleTURNFrame(p []byte) (int, error) { +// If the buffer isn't a valid STUN or ChannelData packet, +// or the length doesn't match return false. +func consumeSingleTURNFrame(b []byte) (int, error) { // Too short to determine if ChannelData or STUN - if len(p) < 9 { + if len(b) < 9 { return 0, errIncompleteTURNFrame } var datagramSize uint16 switch { - case stun.IsMessage(p): - datagramSize = binary.BigEndian.Uint16(p[2:4]) + stunHeaderSize - case proto.ChannelNumber(binary.BigEndian.Uint16(p[0:2])).Valid(): - datagramSize = binary.BigEndian.Uint16(p[channelDataNumberSize:channelDataHeaderSize]) + case stun.IsMessage(b): + datagramSize = binary.BigEndian.Uint16(b[2:4]) + stunHeaderSize + case proto.ChannelNumber(binary.BigEndian.Uint16(b[0:2])).Valid(): + datagramSize = binary.BigEndian.Uint16(b[channelDataNumberSize:channelDataHeaderSize]) if paddingOverflow := (datagramSize + channelDataPadding) % channelDataPadding; paddingOverflow != 0 { datagramSize = (datagramSize + channelDataPadding) - paddingOverflow } datagramSize += channelDataHeaderSize - case len(p) < stunHeaderSize: + case len(b) < stunHeaderSize: return 0, errIncompleteTURNFrame default: return 0, errInvalidTURNFrame } - if len(p) < int(datagramSize) { + if len(b) < int(datagramSize) { return 0, errIncompleteTURNFrame } return int(datagramSize), nil } -// ReadFrom implements ReadFrom from net.PacketConn -func (s *STUNConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { +// ReadFrom implements ReadFrom from net.PacketConn. +func (s *STUNConn) ReadFrom(payload []byte) (n int, addr net.Addr, err error) { // First pass any buffered data from previous reads n, err = consumeSingleTURNFrame(s.buff) if errors.Is(err, errInvalidTURNFrame) { return 0, nil, err } else if err == nil { - copy(p, s.buff[:n]) + copy(payload, s.buff[:n]) s.buff = s.buff[n:] return n, s.nextConn.RemoteAddr(), nil } // Then read from the nextConn, appending to our buff - n, err = s.nextConn.Read(p) + n, err = s.nextConn.Read(payload) if err != nil { return 0, nil, err } - s.buff = append(s.buff, append([]byte{}, p[:n]...)...) - return s.ReadFrom(p) + s.buff = append(s.buff, append([]byte{}, payload[:n]...)...) + + return s.ReadFrom(payload) } -// WriteTo implements WriteTo from net.PacketConn -func (s *STUNConn) WriteTo(p []byte, _ net.Addr) (n int, err error) { - return s.nextConn.Write(p) +// WriteTo implements WriteTo from net.PacketConn. +func (s *STUNConn) WriteTo(payload []byte, _ net.Addr) (n int, err error) { + return s.nextConn.Write(payload) } -// Close implements Close from net.PacketConn +// Close implements Close from net.PacketConn. func (s *STUNConn) Close() error { return s.nextConn.Close() } -// LocalAddr implements LocalAddr from net.PacketConn +// LocalAddr implements LocalAddr from net.PacketConn. func (s *STUNConn) LocalAddr() net.Addr { return s.nextConn.LocalAddr() } -// SetDeadline implements SetDeadline from net.PacketConn +// SetDeadline implements SetDeadline from net.PacketConn. func (s *STUNConn) SetDeadline(t time.Time) error { return s.nextConn.SetDeadline(t) } -// SetReadDeadline implements SetReadDeadline from net.PacketConn +// SetReadDeadline implements SetReadDeadline from net.PacketConn. func (s *STUNConn) SetReadDeadline(t time.Time) error { return s.nextConn.SetReadDeadline(t) } -// SetWriteDeadline implements SetWriteDeadline from net.PacketConn +// SetWriteDeadline implements SetWriteDeadline from net.PacketConn. func (s *STUNConn) SetWriteDeadline(t time.Time) error { return s.nextConn.SetWriteDeadline(t) } -// NewSTUNConn creates a STUNConn +// NewSTUNConn creates a STUNConn. func NewSTUNConn(nextConn net.Conn) *STUNConn { return &STUNConn{nextConn: nextConn} } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/.golangci.yml b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/.golangci.yml index 120faf29b..59edee274 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/.golangci.yml +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/.golangci.yml @@ -41,6 +41,12 @@ linters-settings: - w io.Writer - r io.Reader - b []byte + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false linters: enable: diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/atomicbool.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/atomicbool.go deleted file mode 100644 index 846289eca..000000000 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/atomicbool.go +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The Pion community -// SPDX-License-Identifier: MIT - -package webrtc - -import "sync/atomic" - -type atomicBool struct { - val int32 -} - -func (b *atomicBool) set(value bool) { // nolint: unparam - var i int32 - if value { - i = 1 - } - - atomic.StoreInt32(&(b.val), i) -} - -func (b *atomicBool) get() bool { - return atomic.LoadInt32(&(b.val)) != 0 -} - -func (b *atomicBool) swap(value bool) bool { - var i int32 - if value { - i = 1 - } - - return atomic.SwapInt32(&(b.val), i) != 0 -} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/datachannel_js.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/datachannel_js.go index add07b697..26ff02cdf 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/datachannel_js.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/datachannel_js.go @@ -48,7 +48,7 @@ func (d *DataChannel) OnOpen(f func()) { oldHandler := d.onOpenHandler defer oldHandler.Release() } - onOpenHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onOpenHandler := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -63,7 +63,7 @@ func (d *DataChannel) OnClose(f func()) { oldHandler := d.onCloseHandler defer oldHandler.Release() } - onCloseHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onCloseHandler := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -78,7 +78,7 @@ func (d *DataChannel) OnClosing(f func()) { oldHandler := d.onClosingHandler defer oldHandler.Release() } - onClosingHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onClosingHandler := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -91,7 +91,7 @@ func (d *DataChannel) OnError(f func(err error)) { oldHandler := d.onErrorHandler defer oldHandler.Release() } - onErrorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onErrorHandler := js.FuncOf(func(this js.Value, args []js.Value) any { event := args[0] errorObj := event.Get("error") // FYI RTCError has some extra properties, e.g. `errorDetail`: @@ -111,7 +111,7 @@ func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) { oldHandler := d.onMessageHandler defer oldHandler.Release() } - onMessageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onMessageHandler := js.FuncOf(func(this js.Value, args []js.Value) any { // pion/webrtc/projects/15 data := args[0].Get("data") go func() { @@ -300,7 +300,7 @@ func (d *DataChannel) OnBufferedAmountLow(f func()) { oldHandler := d.onBufferedAmountLow defer oldHandler.Release() } - onBufferedAmountLow := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onBufferedAmountLow := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -336,7 +336,7 @@ func valueToDataChannelMessage(val js.Value) DataChannelMessage { // channel to signal when reading is done. reader := js.Global().Get("FileReader").New() doneChan := make(chan struct{}) - reader.Call("addEventListener", "loadend", js.FuncOf(func(this js.Value, args []js.Value) interface{} { + reader.Call("addEventListener", "loadend", js.FuncOf(func(this js.Value, args []js.Value) any { go func() { // Signal that the FileReader is done reading/loading by sending through // the doneChan. diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/errors.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/errors.go index 539422913..fc5ea2cdb 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/errors.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/errors.go @@ -248,7 +248,7 @@ var ( errRTPSenderRIDCollision = errors.New("Sender cannot encoding due to RID collision") errRTPSenderNoTrackForRID = errors.New("Sender does not have track for RID") - errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil") + errRTPTransceiverCannotChangeMid = errors.New("cannot change transceiver mid") errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending") errRTPTransceiverCodecUnsupported = errors.New("unsupported codec type by this transceiver") diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver.go index 32a368541..7be341aed 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver.go @@ -18,7 +18,7 @@ import ( type ICEServer struct { URLs []string `json:"urls"` Username string `json:"username,omitempty"` - Credential interface{} `json:"credential,omitempty"` + Credential any `json:"credential,omitempty"` CredentialType ICECredentialType `json:"credentialType,omitempty"` } @@ -74,8 +74,8 @@ func (s ICEServer) urls() ([]*stun.URI, error) { //nolint:cyclop return urls, nil } -func iceserverUnmarshalUrls(val interface{}) (*[]string, error) { - s, ok := val.([]interface{}) +func iceserverUnmarshalUrls(val any) (*[]string, error) { + s, ok := val.([]any) if !ok { return nil, errInvalidICEServer } @@ -90,8 +90,8 @@ func iceserverUnmarshalUrls(val interface{}) (*[]string, error) { return &out, nil } -func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) { - c, ok := val.(map[string]interface{}) +func iceserverUnmarshalOauth(val any) (*OAuthCredential, error) { + c, ok := val.(map[string]any) if !ok { return nil, errInvalidICEServer } @@ -110,7 +110,7 @@ func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) { }, nil } -func (s *ICEServer) iceserverUnmarshalFields(fields map[string]interface{}) error { //nolint:cyclop +func (s *ICEServer) iceserverUnmarshalFields(fields map[string]any) error { //nolint:cyclop if val, ok := fields["urls"]; ok { u, err := iceserverUnmarshalUrls(val) if err != nil { @@ -160,12 +160,12 @@ func (s *ICEServer) iceserverUnmarshalFields(fields map[string]interface{}) erro // UnmarshalJSON parses the JSON-encoded data and stores the result. func (s *ICEServer) UnmarshalJSON(b []byte) error { - var tmp interface{} + var tmp any err := json.Unmarshal(b, &tmp) if err != nil { return err } - if m, ok := tmp.(map[string]interface{}); ok { + if m, ok := tmp.(map[string]any); ok { return s.iceserverUnmarshalFields(m) } @@ -174,7 +174,7 @@ func (s *ICEServer) UnmarshalJSON(b []byte) error { // MarshalJSON returns the JSON encoding. func (s ICEServer) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}) + m := make(map[string]any) m["urls"] = s.URLs if s.Username != "" { m["username"] = s.Username diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver_js.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver_js.go index 2f293dde0..f121349d0 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver_js.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/iceserver_js.go @@ -18,7 +18,7 @@ type ICEServer struct { URLs []string Username string // Note: TURN is not supported in the WASM bindings yet - Credential interface{} + Credential any CredentialType ICECredentialType } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/interceptor.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/interceptor.go index d2df622f9..5d7dea4c8 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/interceptor.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/interceptor.go @@ -10,6 +10,7 @@ import ( "sync/atomic" "github.com/pion/interceptor" + "github.com/pion/interceptor/pkg/flexfec" "github.com/pion/interceptor/pkg/nack" "github.com/pion/interceptor/pkg/report" "github.com/pion/interceptor/pkg/rfc8888" @@ -160,6 +161,41 @@ func ConfigureSimulcastExtensionHeaders(mediaEngine *MediaEngine) error { ) } +// ConfigureFlexFEC03 registers flexfec-03 codec with provided payloadType in mediaEngine +// and adds corresponding interceptor to the registry. +// Note that this function should be called before any other interceptor that modifies RTP packets +// (i.e. TWCCHeaderExtensionSender) is added to the registry, so that packets generated by flexfec +// interceptor are not modified. +func ConfigureFlexFEC03( + payloadType PayloadType, + mediaEngine *MediaEngine, + interceptorRegistry *interceptor.Registry, + options ...flexfec.FecOption, +) error { + codecFEC := RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeFlexFEC03, + ClockRate: 90000, + SDPFmtpLine: "repair-window=10000000", + RTCPFeedback: nil, + }, + PayloadType: payloadType, + } + + if err := mediaEngine.RegisterCodec(codecFEC, RTPCodecTypeVideo); err != nil { + return err + } + + generator, err := flexfec.NewFecInterceptor(options...) + if err != nil { + return err + } + + interceptorRegistry.Add(generator) + + return nil +} + type interceptorToTrackLocalWriter struct{ interceptor atomic.Value } // interceptor.RTPWriter } func (i *interceptorToTrackLocalWriter) WriteRTP(header *rtp.Header, payload []byte) (int, error) { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/js_utils.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/js_utils.go index 7e7b5e190..ee3120b65 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/js_utils.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/js_utils.go @@ -19,7 +19,7 @@ func awaitPromise(promise js.Value) (js.Value, error) { resultsChan := make(chan js.Value) errChan := make(chan js.Error) - thenFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + thenFunc := js.FuncOf(func(this js.Value, args []js.Value) any { go func() { resultsChan <- args[0] }() @@ -27,7 +27,7 @@ func awaitPromise(promise js.Value) (js.Value, error) { }) defer thenFunc.Release() - catchFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + catchFunc := js.FuncOf(func(this js.Value, args []js.Value) any { go func() { errChan <- js.Error{args[0]} }() @@ -75,7 +75,7 @@ func uint8ToValueOrUndefined(val uint8) js.Value { return js.ValueOf(val) } -func interfaceToValueOrUndefined(val interface{}) js.Value { +func interfaceToValueOrUndefined(val any) js.Value { if val == nil { return js.Undefined() } @@ -140,7 +140,7 @@ func boolPointerToValue(val *bool) js.Value { } func stringsToValue(strings []string) js.Value { - val := make([]interface{}, len(strings)) + val := make([]any, len(strings)) for i, s := range strings { val[i] = s } @@ -155,7 +155,7 @@ func stringEnumToValueOrUndefined(s string) js.Value { } // Converts the return value of recover() to an error. -func recoveryToError(e interface{}) error { +func recoveryToError(e any) error { switch e := e.(type) { case error: return e diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/operations.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/operations.go index 3e65f1357..a86850f57 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/operations.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/operations.go @@ -6,6 +6,7 @@ package webrtc import ( "container/list" "sync" + "sync/atomic" ) // Operation is a function. @@ -17,13 +18,13 @@ type operations struct { busyCh chan struct{} ops *list.List - updateNegotiationNeededFlagOnEmptyChain *atomicBool + updateNegotiationNeededFlagOnEmptyChain *atomic.Bool onNegotiationNeeded func() isClosed bool } func newOperations( - updateNegotiationNeededFlagOnEmptyChain *atomicBool, + updateNegotiationNeededFlagOnEmptyChain *atomic.Bool, onNegotiationNeeded func(), ) *operations { return &operations{ @@ -150,9 +151,9 @@ func (o *operations) start() { fn() fn = o.pop() } - if !o.updateNegotiationNeededFlagOnEmptyChain.get() { + if !o.updateNegotiationNeededFlagOnEmptyChain.Load() { return } - o.updateNegotiationNeededFlagOnEmptyChain.set(false) + o.updateNegotiationNeededFlagOnEmptyChain.Store(false) o.onNegotiationNeeded() } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/package.json b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/package.json index f58d23972..642ff8406 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/package.json +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/package.json @@ -3,7 +3,7 @@ "repository": "git@github.com:pion/webrtc.git", "private": true, "devDependencies": { - "@roamhq/wrtc": "^0.8.0" + "@roamhq/wrtc": "^0.9.0" }, "dependencies": { "request": "2.88.2" diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection.go index a2d1a7afb..50b05cd05 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection.go @@ -55,12 +55,12 @@ type PeerConnection struct { idpLoginURL *string - isClosed *atomicBool + isClosed *atomic.Bool isGracefullyClosingOrClosed bool isCloseDone chan struct{} isGracefulCloseDone chan struct{} - isNegotiationNeeded *atomicBool - updateNegotiationNeededFlagOnEmptyChain *atomicBool + isNegotiationNeeded *atomic.Bool + updateNegotiationNeededFlagOnEmptyChain *atomic.Bool lastOffer string lastAnswer string @@ -124,11 +124,11 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, Certificates: []Certificate{}, ICECandidatePoolSize: 0, }, - isClosed: &atomicBool{}, + isClosed: &atomic.Bool{}, isCloseDone: make(chan struct{}), isGracefulCloseDone: make(chan struct{}), - isNegotiationNeeded: &atomicBool{}, - updateNegotiationNeededFlagOnEmptyChain: &atomicBool{}, + isNegotiationNeeded: &atomic.Bool{}, + updateNegotiationNeededFlagOnEmptyChain: &atomic.Bool{}, lastOffer: "", lastAnswer: "", greaterMid: -1, @@ -296,7 +296,7 @@ func (pc *PeerConnection) onNegotiationNeeded() { // 4.7.3.1 If the length of connection.[[Operations]] is not 0, then set // connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps. if !pc.ops.IsEmpty() { - pc.updateNegotiationNeededFlagOnEmptyChain.set(true) + pc.updateNegotiationNeededFlagOnEmptyChain.Store(true) return } @@ -306,7 +306,7 @@ func (pc *PeerConnection) onNegotiationNeeded() { // https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag func (pc *PeerConnection) negotiationNeededOp() { // 4.7.3.2.1 If connection.[[IsClosed]] is true, abort these steps. - if pc.isClosed.get() { + if pc.isClosed.Load() { return } @@ -314,7 +314,7 @@ func (pc *PeerConnection) negotiationNeededOp() { // then set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to // true, and abort these steps. if !pc.ops.IsEmpty() { - pc.updateNegotiationNeededFlagOnEmptyChain.set(true) + pc.updateNegotiationNeededFlagOnEmptyChain.Store(true) return } @@ -328,18 +328,18 @@ func (pc *PeerConnection) negotiationNeededOp() { // clear the negotiation-needed flag by setting connection.[[NegotiationNeeded]] // to false, and abort these steps. if !pc.checkNegotiationNeeded() { - pc.isNegotiationNeeded.set(false) + pc.isNegotiationNeeded.Store(false) return } // 4.7.3.2.5 If connection.[[NegotiationNeeded]] is already true, abort these steps. - if pc.isNegotiationNeeded.get() { + if pc.isNegotiationNeeded.Load() { return } // 4.7.3.2.6 Set connection.[[NegotiationNeeded]] to true. - pc.isNegotiationNeeded.set(true) + pc.isNegotiationNeeded.Store(true) // 4.7.3.2.7 Fire an event named negotiationneeded at connection. if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); ok && handler != nil { @@ -513,7 +513,7 @@ func (pc *PeerConnection) onConnectionStateChange(cs PeerConnectionState) { // SetConfiguration updates the configuration of this PeerConnection object. func (pc *PeerConnection) SetConfiguration(configuration Configuration) error { //nolint:gocognit,cyclop // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2) - if pc.isClosed.get() { + if pc.isClosed.Load() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -623,7 +623,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription switch { case useIdentity: return SessionDescription{}, errIdentityProviderNotImplemented - case pc.isClosed.get(): + case pc.isClosed.Load(): return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -763,7 +763,7 @@ func (pc *PeerConnection) updateConnectionState( connectionState := PeerConnectionStateNew switch { // The RTCPeerConnection object's [[IsClosed]] slot is true. - case pc.isClosed.get(): + case pc.isClosed.Load(): connectionState = PeerConnectionStateClosed // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state. @@ -844,7 +844,7 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrNoRemoteDescription} case useIdentity: return SessionDescription{}, errIdentityProviderNotImplemented - case pc.isClosed.get(): + case pc.isClosed.Load(): return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} case pc.signalingState.Get() != SignalingStateHaveRemoteOffer && pc.signalingState.Get() != SignalingStateHaveLocalPranswer: @@ -891,7 +891,7 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro //nolint:gocognit,cyclop func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeOp) error { switch { - case pc.isClosed.get(): + case pc.isClosed.Load(): return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} case NewSDPType(sd.Type.String()) == SDPTypeUnknown: return &rtcerr.TypeError{ @@ -995,7 +995,7 @@ func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeO if err == nil { pc.signalingState.Set(nextState) if pc.signalingState.Get() == SignalingStateStable { - pc.isNegotiationNeeded.set(false) + pc.isNegotiationNeeded.Store(false) pc.mu.Lock() pc.onNegotiationNeeded() pc.mu.Unlock() @@ -1010,7 +1010,7 @@ func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeO // //nolint:cyclop func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { - if pc.isClosed.get() { + if pc.isClosed.Load() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -1081,7 +1081,7 @@ func (pc *PeerConnection) LocalDescription() *SessionDescription { // //nolint:gocognit,gocyclo,cyclop,maintidx func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { - if pc.isClosed.get() { + if pc.isClosed.Load() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -1700,7 +1700,10 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err // If a SSRC already exists in the RemoteDescription don't perform heuristics upon it for _, track := range trackDetailsFromSDP(pc.log, remoteDescription.parsed) { - if track.repairSsrc != nil && ssrc == *track.repairSsrc { + if track.rtxSsrc != nil && ssrc == *track.rtxSsrc { + return nil + } + if track.fecSsrc != nil && ssrc == *track.fecSsrc { return nil } for _, trackSsrc := range track.ssrcs { @@ -1883,7 +1886,7 @@ func (pc *PeerConnection) undeclaredRTPMediaProcessor() { //nolint:cyclop return } - if pc.isClosed.get() { + if pc.isClosed.Load() { if err = srtpReadStream.Close(); err != nil { pc.log.Warnf("Failed to close RTP stream %v", err) } @@ -2073,7 +2076,7 @@ func (pc *PeerConnection) GetTransceivers() []*RTPTransceiver { // //nolint:cyclop func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { - if pc.isClosed.get() { + if pc.isClosed.Load() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2115,7 +2118,7 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { // RemoveTrack removes a Track from the PeerConnection. func (pc *PeerConnection) RemoveTrack(sender *RTPSender) (err error) { - if pc.isClosed.get() { + if pc.isClosed.Load() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2183,7 +2186,7 @@ func (pc *PeerConnection) AddTransceiverFromKind( kind RTPCodecType, init ...RTPTransceiverInit, ) (t *RTPTransceiver, err error) { - if pc.isClosed.get() { + if pc.isClosed.Load() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2228,7 +2231,7 @@ func (pc *PeerConnection) AddTransceiverFromTrack( track TrackLocal, init ...RTPTransceiverInit, ) (t *RTPTransceiver, err error) { - if pc.isClosed.get() { + if pc.isClosed.Load() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2256,7 +2259,7 @@ func (pc *PeerConnection) AddTransceiverFromTrack( //nolint:cyclop func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (*DataChannel, error) { // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #2) - if pc.isClosed.get() { + if pc.isClosed.Load() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2377,7 +2380,7 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error { //nolint:cyc // some overlapping close cases when both normal and graceful close are used // that should be idempotent, but be cautioned when writing new close behavior // to preserve this property. - isAlreadyClosingOrClosed := pc.isClosed.swap(true) + isAlreadyClosingOrClosed := pc.isClosed.Swap(true) isAlreadyGracefullyClosingOrClosed := pc.isGracefullyClosingOrClosed if shouldGracefullyClose && !isAlreadyGracefullyClosingOrClosed { pc.isGracefullyClosingOrClosed = true @@ -2665,7 +2668,7 @@ func (pc *PeerConnection) startTransports( } pc.dtlsTransport.internalOnCloseHandler = func() { - if pc.isClosed.get() || pc.api.settingEngine.disableCloseByDTLS { + if pc.isClosed.Load() || pc.api.settingEngine.disableCloseByDTLS { return } @@ -2718,7 +2721,7 @@ func (pc *PeerConnection) generateUnmatchedSDP( if err != nil { return nil, err } - desc.Attributes = append(desc.Attributes, sdp.Attribute{Key: sdp.AttrKeyMsidSemantic, Value: "WMS*"}) + desc.Attributes = append(desc.Attributes, sdp.Attribute{Key: sdp.AttrKeyMsidSemantic, Value: "WMS *"}) iceParams, err := pc.iceGatherer.GetLocalParameters() if err != nil { @@ -2811,7 +2814,7 @@ func (pc *PeerConnection) generateMatchedSDP( if err != nil { return nil, err } - desc.Attributes = append(desc.Attributes, sdp.Attribute{Key: sdp.AttrKeyMsidSemantic, Value: "WMS*"}) + desc.Attributes = append(desc.Attributes, sdp.Attribute{Key: sdp.AttrKeyMsidSemantic, Value: "WMS *"}) iceParams, err := pc.iceGatherer.GetLocalParameters() if err != nil { diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection_js.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection_js.go index c7e968ca2..c332930bc 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection_js.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/peerconnection_js.go @@ -71,7 +71,7 @@ func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) { oldHandler := pc.onSignalingStateChangeHandler defer oldHandler.Release() } - onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) any { state := newSignalingState(args[0].String()) go f(state) return js.Undefined() @@ -87,7 +87,7 @@ func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) { oldHandler := pc.onDataChannelHandler defer oldHandler.Release() } - onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) any { // pion/webrtc/projects/15 // This reference to the underlying DataChannel doesn't know // about any other references to the same DataChannel. This might result in @@ -112,7 +112,7 @@ func (pc *PeerConnection) OnNegotiationNeeded(f func()) { oldHandler := pc.onNegotiationNeededHandler defer oldHandler.Release() } - onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -127,7 +127,7 @@ func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) oldHandler := pc.onICEConnectionStateChangeHandler defer oldHandler.Release() } - onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) any { connectionState := NewICEConnectionState(pc.underlying.Get("iceConnectionState").String()) go f(connectionState) return js.Undefined() @@ -143,7 +143,7 @@ func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) { oldHandler := pc.onConnectionStateChangeHandler defer oldHandler.Release() } - onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) any { connectionState := newPeerConnectionState(pc.underlying.Get("connectionState").String()) go f(connectionState) return js.Undefined() @@ -335,7 +335,7 @@ func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) { oldHandler := pc.onICECandidateHandler defer oldHandler.Release() } - onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) any { candidate := valueToICECandidate(args[0].Get("candidate")) if candidate == nil && pc.onGatherCompleteHandler != nil { go pc.onGatherCompleteHandler() @@ -355,7 +355,7 @@ func (pc *PeerConnection) OnICEGatheringStateChange(f func()) { oldHandler := pc.onICEGatheringStateChangeHandler defer oldHandler.Release() } - onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) any { go f() return js.Undefined() }) @@ -545,7 +545,7 @@ func (pc *PeerConnection) SCTP() *SCTPTransport { // through to the JavaScript WebRTC API. Any zero values are converted to // js.Undefined(), which will result in the default value being used. func configurationToValue(configuration Configuration) js.Value { - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "iceServers": iceServersToValue(configuration.ICEServers), "iceTransportPolicy": stringEnumToValueOrUndefined(configuration.ICETransportPolicy.String()), "bundlePolicy": stringEnumToValueOrUndefined(configuration.BundlePolicy.String()), @@ -562,7 +562,7 @@ func iceServersToValue(iceServers []ICEServer) js.Value { if len(iceServers) == 0 { return js.Undefined() } - maps := make([]interface{}, len(iceServers)) + maps := make([]any, len(iceServers)) for i, server := range iceServers { maps[i] = iceServerToValue(server) } @@ -570,7 +570,7 @@ func iceServersToValue(iceServers []ICEServer) js.Value { } func oauthCredentialToValue(o OAuthCredential) js.Value { - out := map[string]interface{}{ + out := map[string]any{ "MACKey": o.MACKey, "AccessToken": o.AccessToken, } @@ -578,7 +578,7 @@ func oauthCredentialToValue(o OAuthCredential) js.Value { } func iceServerToValue(server ICEServer) js.Value { - out := map[string]interface{}{ + out := map[string]any{ "urls": stringsToValue(server.URLs), // required } if server.Username != "" { @@ -624,7 +624,7 @@ func valueToICEServers(iceServersValue js.Value) []ICEServer { return iceServers } -func valueToICECredential(iceCredentialValue js.Value) interface{} { +func valueToICECredential(iceCredentialValue js.Value) any { if iceCredentialValue.IsNull() || iceCredentialValue.IsUndefined() { return nil } @@ -704,7 +704,7 @@ func sessionDescriptionToValue(desc *SessionDescription) js.Value { if desc == nil { return js.Undefined() } - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "type": desc.Type.String(), "sdp": desc.SDP, }) @@ -724,7 +724,7 @@ func offerOptionsToValue(offerOptions *OfferOptions) js.Value { if offerOptions == nil { return js.Undefined() } - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "iceRestart": offerOptions.ICERestart, "voiceActivityDetection": offerOptions.VoiceActivityDetection, }) @@ -734,13 +734,13 @@ func answerOptionsToValue(answerOptions *AnswerOptions) js.Value { if answerOptions == nil { return js.Undefined() } - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "voiceActivityDetection": answerOptions.VoiceActivityDetection, }) } func iceCandidateInitToValue(candidate ICECandidateInit) js.Value { - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "candidate": candidate.Candidate, "sdpMid": stringPointerToValue(candidate.SDPMid), "sdpMLineIndex": uint16PointerToValue(candidate.SDPMLineIndex), @@ -754,7 +754,7 @@ func dataChannelInitToValue(options *DataChannelInit) js.Value { } maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime) - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "ordered": boolPointerToValue(options.Ordered), "maxPacketLifeTime": maxPacketLifeTime, // See https://bugs.chromium.org/p/chromium/issues/detail?id=696681 @@ -768,7 +768,7 @@ func dataChannelInitToValue(options *DataChannelInit) js.Value { } func rtpTransceiverInitInitToValue(init RTPTransceiverInit) js.Value { - return js.ValueOf(map[string]interface{}{ + return js.ValueOf(map[string]any{ "direction": init.Direction.String(), }) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/h265reader.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/h265reader.go new file mode 100644 index 000000000..5131ac68b --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/h265reader.go @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package h265reader implements a H265/HEVC Annex-B Reader +package h265reader + +import ( + "bytes" + "errors" + "io" +) + +// H265Reader reads data from stream and constructs h265 nal units. +type H265Reader struct { + stream io.Reader + nalBuffer []byte + countOfConsecutiveZeroBytes int + nalPrefixParsed bool + readBuffer []byte + tmpReadBuf []byte +} + +var ( + errNilReader = errors.New("stream is nil") + errDataIsNotH265Stream = errors.New("data is not a H265/HEVC bitstream") +) + +// NewReader creates new H265Reader. +func NewReader(in io.Reader) (*H265Reader, error) { + if in == nil { + return nil, errNilReader + } + + reader := &H265Reader{ + stream: in, + nalBuffer: make([]byte, 0), + nalPrefixParsed: false, + readBuffer: make([]byte, 0), + tmpReadBuf: make([]byte, 4096), + } + + return reader, nil +} + +// NAL H.265/HEVC Network Abstraction Layer. +type NAL struct { + PictureOrderCount uint32 + + /* 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 | + +-------------+-----------------+ + */ + ForbiddenZeroBit bool + NalUnitType NalUnitType + LayerID uint8 + TemporalIDPlus1 uint8 + + Data []byte // header bytes + rbsp +} + +func (reader *H265Reader) read(numToRead int) (data []byte, e error) { + for len(reader.readBuffer) < numToRead { + n, err := reader.stream.Read(reader.tmpReadBuf) + if err != nil { + return nil, err + } + if n == 0 { + break + } + reader.readBuffer = append(reader.readBuffer, reader.tmpReadBuf[0:n]...) + } + var numShouldRead int + if numToRead <= len(reader.readBuffer) { + numShouldRead = numToRead + } else { + numShouldRead = len(reader.readBuffer) + } + data = reader.readBuffer[0:numShouldRead] + reader.readBuffer = reader.readBuffer[numShouldRead:] + + return data, nil +} + +func (reader *H265Reader) bitStreamStartsWithH265Prefix() (prefixLength int, e error) { + nalPrefix3Bytes := []byte{0, 0, 1} + nalPrefix4Bytes := []byte{0, 0, 0, 1} + + prefixBuffer, e := reader.read(4) + if e != nil { + return prefixLength, e + } + + n := len(prefixBuffer) + + if n == 0 { + return 0, io.EOF + } + + if n < 3 { + return 0, errDataIsNotH265Stream + } + + nalPrefix3BytesFound := bytes.Equal(nalPrefix3Bytes, prefixBuffer[:3]) + if n == 3 { + if nalPrefix3BytesFound { + return 0, io.EOF + } + + return 0, errDataIsNotH265Stream + } + + // n == 4 + if nalPrefix3BytesFound { + reader.nalBuffer = append(reader.nalBuffer, prefixBuffer[3]) + + return 3, nil + } + + nalPrefix4BytesFound := bytes.Equal(nalPrefix4Bytes, prefixBuffer) + if nalPrefix4BytesFound { + return 4, nil + } + + return 0, errDataIsNotH265Stream +} + +// NextNAL reads from stream and returns then next NAL, +// and an error if there is incomplete frame data. +// Returns all nil values when no more NALs are available. +func (reader *H265Reader) NextNAL() (*NAL, error) { + if !reader.nalPrefixParsed { + _, err := reader.bitStreamStartsWithH265Prefix() + if err != nil { + return nil, err + } + + reader.nalPrefixParsed = true + } + + for { + buffer, err := reader.read(1) + if err != nil { + break + } + + n := len(buffer) + + if n != 1 { + break + } + readByte := buffer[0] + nalFound := reader.processByte(readByte) + if nalFound { + naluType := NalUnitType((reader.nalBuffer[0] & 0x7E) >> 1) + if naluType == NalUnitTypePrefixSei || naluType == NalUnitTypeSuffixSei { + reader.nalBuffer = nil + + continue + } + + break + } + + reader.nalBuffer = append(reader.nalBuffer, readByte) + } + + if len(reader.nalBuffer) == 0 { + return nil, io.EOF + } + + nal := newNal(reader.nalBuffer) + reader.nalBuffer = nil + nal.parseHeader() + + return nal, nil +} + +func (reader *H265Reader) processByte(readByte byte) (nalFound bool) { + nalFound = false + + switch readByte { + case 0: + reader.countOfConsecutiveZeroBytes++ + case 1: + if reader.countOfConsecutiveZeroBytes >= 2 { + countOfConsecutiveZeroBytesInPrefix := 2 + if reader.countOfConsecutiveZeroBytes > 2 { + countOfConsecutiveZeroBytesInPrefix = 3 + } + + if nalUnitLength := len(reader.nalBuffer) - countOfConsecutiveZeroBytesInPrefix; nalUnitLength > 0 { + reader.nalBuffer = reader.nalBuffer[0:nalUnitLength] + nalFound = true + } + } + + reader.countOfConsecutiveZeroBytes = 0 + default: + reader.countOfConsecutiveZeroBytes = 0 + } + + return nalFound +} + +func newNal(data []byte) *NAL { + return &NAL{ + PictureOrderCount: 0, + ForbiddenZeroBit: false, + NalUnitType: NalUnitTypeTrailN, + LayerID: 0, + TemporalIDPlus1: 0, + Data: data, + } +} + +func (h *NAL) parseHeader() { + if len(h.Data) < 2 { + return + } + + // H.265 NAL header is 2 bytes + firstByte := h.Data[0] + secondByte := h.Data[1] + + h.ForbiddenZeroBit = (firstByte & 0x80) != 0 + h.NalUnitType = NalUnitType((firstByte & 0x7E) >> 1) + h.LayerID = ((firstByte & 0x01) << 5) | ((secondByte & 0xF8) >> 3) + h.TemporalIDPlus1 = secondByte & 0x07 +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/nalunittype.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/nalunittype.go new file mode 100644 index 000000000..e0a178bc2 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/h265reader/nalunittype.go @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package h265reader + +import "strconv" + +// NalUnitType is the type of a NAL unit in H.265/HEVC. +type NalUnitType uint8 + +// Enums for H.265/HEVC NAL unit types. +const ( + // VCL NAL unit types. + NalUnitTypeTrailN NalUnitType = 0 // Coded slice segment of a non-TSA, non-STSA trailing picture + NalUnitTypeTrailR NalUnitType = 1 // Coded slice segment of a non-TSA, non-STSA trailing picture + NalUnitTypeTsaN NalUnitType = 2 // Coded slice segment of a TSA picture + NalUnitTypeTsaR NalUnitType = 3 // Coded slice segment of a TSA picture + NalUnitTypeStsaN NalUnitType = 4 // Coded slice segment of an STSA picture + NalUnitTypeStsaR NalUnitType = 5 // Coded slice segment of an STSA picture + NalUnitTypeRadlN NalUnitType = 6 // Coded slice segment of a RADL picture + NalUnitTypeRadlR NalUnitType = 7 // Coded slice segment of a RADL picture + NalUnitTypeRaslN NalUnitType = 8 // Coded slice segment of a RASL picture + NalUnitTypeRaslR NalUnitType = 9 // Coded slice segment of a RASL picture + NalUnitTypeBlaWLp NalUnitType = 16 // Coded slice segment of a BLA picture + NalUnitTypeBlaWRadl NalUnitType = 17 // Coded slice segment of a BLA picture + NalUnitTypeBlaNLp NalUnitType = 18 // Coded slice segment of a BLA picture + NalUnitTypeIdrWRadl NalUnitType = 19 // Coded slice segment of an IDR picture + NalUnitTypeIdrNLp NalUnitType = 20 // Coded slice segment of an IDR picture + NalUnitTypeCraNut NalUnitType = 21 // Coded slice segment of a CRA picture + + // Non-VCL NAL unit types. + NalUnitTypeVps NalUnitType = 32 // Video parameter set + NalUnitTypeSps NalUnitType = 33 // Sequence parameter set + NalUnitTypePps NalUnitType = 34 // Picture parameter set + NalUnitTypeAud NalUnitType = 35 // Access unit delimiter + NalUnitTypeEos NalUnitType = 36 // End of sequence + NalUnitTypeEob NalUnitType = 37 // End of bitstream + NalUnitTypeFd NalUnitType = 38 // Filler data + NalUnitTypePrefixSei NalUnitType = 39 // Supplemental enhancement information + NalUnitTypeSuffixSei NalUnitType = 40 // Supplemental enhancement information + + // Reserved. + NalUnitTypeReserved41 NalUnitType = 41 + NalUnitTypeReserved47 NalUnitType = 47 + NalUnitTypeUnspec48 NalUnitType = 48 + NalUnitTypeUnspec63 NalUnitType = 63 +) + +func (n *NalUnitType) String() string { //nolint:cyclop + var str string + switch *n { + case NalUnitTypeTrailN: + str = "TrailN" + case NalUnitTypeTrailR: + str = "TrailR" + case NalUnitTypeTsaN: + str = "TsaN" + case NalUnitTypeTsaR: + str = "TsaR" + case NalUnitTypeStsaN: + str = "StsaN" + case NalUnitTypeStsaR: + str = "StsaR" + case NalUnitTypeRadlN: + str = "RadlN" + case NalUnitTypeRadlR: + str = "RadlR" + case NalUnitTypeRaslN: + str = "RaslN" + case NalUnitTypeRaslR: + str = "RaslR" + case NalUnitTypeBlaWLp: + str = "BlaWLp" + case NalUnitTypeBlaWRadl: + str = "BlaWRadl" + case NalUnitTypeBlaNLp: + str = "BlaNLp" + case NalUnitTypeIdrWRadl: + str = "IdrWRadl" + case NalUnitTypeIdrNLp: + str = "IdrNLp" + case NalUnitTypeCraNut: + str = "CraNut" + case NalUnitTypeVps: + str = "VPS" + case NalUnitTypeSps: + str = "SPS" + case NalUnitTypePps: + str = "PPS" + case NalUnitTypeAud: + str = "AUD" + case NalUnitTypeEos: + str = "EOS" + case NalUnitTypeEob: + str = "EOB" + case NalUnitTypeFd: + str = "FD" + case NalUnitTypePrefixSei: + str = "PrefixSEI" + case NalUnitTypeSuffixSei: + str = "SuffixSEI" + default: + switch { + case *n >= NalUnitTypeReserved41 && *n <= NalUnitTypeReserved47: + str = "Reserved" + case *n >= NalUnitTypeUnspec48 && *n <= NalUnitTypeUnspec63: + str = "Unspecified" + default: + str = "Unknown" + } + } + str = str + "(" + strconv.FormatInt(int64(*n), 10) + ")" + + return str +} diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/media.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/media.go index f8ee5e382..3da1de7f8 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/media.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/pkg/media/media.go @@ -17,7 +17,7 @@ type Sample struct { Duration time.Duration PacketTimestamp uint32 PrevDroppedPackets uint16 - Metadata interface{} + Metadata any // RTP headers of RTP packets forming this Sample. (Optional) // Useful for accessing RTP extensions associated to the Sample. diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/rtpreceiver.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/rtpreceiver.go index 109f88a4a..0e85b2d95 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/rtpreceiver.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/rtpreceiver.go @@ -61,7 +61,7 @@ type RTPReceiver struct { tracks []trackStreams - closed, received chan interface{} + closed, received chan any mu sync.RWMutex tr *RTPTransceiver @@ -82,10 +82,10 @@ func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTransport) (*RT kind: kind, transport: transport, api: api, - closed: make(chan interface{}), - received: make(chan interface{}), + closed: make(chan any), + received: make(chan any), tracks: []trackStreams{}, - rtxPool: sync.Pool{New: func() interface{} { + rtxPool: sync.Pool{New: func() any { return make([]byte, api.settingEngine.getReceiveMTU()) }}, } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/sdp.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/sdp.go index fa10b6951..6c0b7668b 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/sdp.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/sdp.go @@ -23,13 +23,14 @@ import ( // trackDetails represents any media source that can be represented in a SDP // This isn't keyed by SSRC because it also needs to support rid based sources. type trackDetails struct { - mid string - kind RTPCodecType - streamID string - id string - ssrcs []SSRC - repairSsrc *SSRC - rids []string + mid string + kind RTPCodecType + streamID string + id string + ssrcs []SSRC + rtxSsrc *SSRC + fecSsrc *SSRC + rids []string } func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackDetails { @@ -91,6 +92,7 @@ func trackDetailsFromSDP( for _, media := range s.MediaDescriptions { tracksInMediaSection := []trackDetails{} rtxRepairFlows := map[uint64]uint64{} + fecRepairFlows := map[uint64]uint64{} // Plan B can have multiple tracks in a single media section streamID := "" @@ -143,7 +145,35 @@ func trackDetailsFromSDP( for i := range tracksInMediaSection { if tracksInMediaSection[i].ssrcs[0] == SSRC(baseSsrc) { repairSsrc := SSRC(rtxRepairFlow) - tracksInMediaSection[i].repairSsrc = &repairSsrc + tracksInMediaSection[i].rtxSsrc = &repairSsrc + } + } + } + } else if split[0] == sdp.SemanticTokenForwardErrorCorrectionFramework { + // Similar to above, lines like `a=ssrc-group:FEC-FR aaaaa bbbbb` + // means for video ssrc aaaaa, there's a FEC track bbbbb + if len(split) == 3 { + baseSsrc, err := strconv.ParseUint(split[1], 10, 32) + if err != nil { + log.Warnf("Failed to parse SSRC: %v", err) + + continue + } + fecRepairFlow, err := strconv.ParseUint(split[2], 10, 32) + if err != nil { + log.Warnf("Failed to parse SSRC: %v", err) + + continue + } + fecRepairFlows[fecRepairFlow] = baseSsrc + tracksInMediaSection = filterTrackWithSSRC( + tracksInMediaSection, + SSRC(fecRepairFlow), + ) // Remove if fec was added as track before + for i := range tracksInMediaSection { + if tracksInMediaSection[i].ssrcs[0] == SSRC(baseSsrc) { + repairSsrc := SSRC(fecRepairFlow) + tracksInMediaSection[i].fecSsrc = &repairSsrc } } } @@ -171,6 +201,9 @@ func trackDetailsFromSDP( if _, ok := rtxRepairFlows[ssrc]; ok { continue // This ssrc is a RTX repair flow, ignore } + if _, ok := fecRepairFlows[ssrc]; ok { + continue // This ssrc is a FEC repair flow, ignore + } if len(split) == 3 && strings.HasPrefix(split[1], "msid:") { streamID = split[1][len("msid:"):] @@ -197,7 +230,13 @@ func trackDetailsFromSDP( for r, baseSsrc := range rtxRepairFlows { if baseSsrc == ssrc { repairSsrc := SSRC(r) //nolint:gosec // G115 - trackDetails.repairSsrc = &repairSsrc + trackDetails.rtxSsrc = &repairSsrc + } + } + for r, baseSsrc := range fecRepairFlows { + if baseSsrc == ssrc { + fecSsrc := SSRC(r) //nolint:gosec // G115 + trackDetails.fecSsrc = &fecSsrc } } @@ -243,8 +282,12 @@ func trackDetailsToRTPReceiveParameters(trackDetails *trackDetails) RTPReceivePa encodings[i].SSRC = trackDetails.ssrcs[i] } - if trackDetails.repairSsrc != nil { - encodings[i].RTX.SSRC = *trackDetails.repairSsrc + if trackDetails.rtxSsrc != nil { + encodings[i].RTX.SSRC = *trackDetails.rtxSsrc + } + + if trackDetails.fecSsrc != nil { + encodings[i].FEC.SSRC = *trackDetails.fecSsrc } } @@ -430,10 +473,26 @@ func addSenderSDP( sendParameters := sender.GetParameters() for _, encoding := range sendParameters.Encodings { if encoding.RTX.SSRC != 0 { - media = media.WithValueAttribute("ssrc-group", fmt.Sprintf("FID %d %d", encoding.SSRC, encoding.RTX.SSRC)) + media = media.WithValueAttribute( + "ssrc-group", + fmt.Sprintf( + "%s %d %d", + sdp.SemanticTokenFlowIdentification, + encoding.SSRC, + encoding.RTX.SSRC, + ), + ) } if encoding.FEC.SSRC != 0 { - media = media.WithValueAttribute("ssrc-group", fmt.Sprintf("FEC-FR %d %d", encoding.SSRC, encoding.FEC.SSRC)) + media = media.WithValueAttribute( + "ssrc-group", + fmt.Sprintf( + "%s %d %d", + sdp.SemanticTokenForwardErrorCorrectionFramework, + encoding.SSRC, + encoding.FEC.SSRC, + ), + ) } media = media.WithMediaSource( diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/track_local_static.go b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/track_local_static.go index 6bac1e1b3..f590d8119 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/track_local_static.go +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/track_local_static.go @@ -154,7 +154,7 @@ func (s *TrackLocalStaticRTP) Codec() RTPCodecCapability { // packetPool is a pool of packets used by WriteRTP and Write below // nolint:gochecknoglobals var rtpPacketPool = sync.Pool{ - New: func() interface{} { + New: func() any { return &rtp.Packet{} }, } @@ -194,6 +194,11 @@ func (s *TrackLocalStaticRTP) writeRTP(packet *rtp.Packet) error { for _, b := range s.bindings { packet.Header.SSRC = uint32(b.ssrc) packet.Header.PayloadType = uint8(b.payloadType) + // b.writeStream.WriteRTP below expects header and payload separately, so value of Packet.PaddingSize + // would be lost. Copy it to Packet.Header.PaddingSize to avoid that problem. + if packet.PaddingSize != 0 && packet.Header.PaddingSize == 0 { + packet.Header.PaddingSize = packet.PaddingSize + } if _, err := b.writeStream.WriteRTP(&packet.Header, packet.Payload); err != nil { writeErrs = append(writeErrs, err) } diff --git a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/yarn.lock b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/yarn.lock index 809d55604..bea781e0a 100644 --- a/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/yarn.lock +++ b/trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v4/yarn.lock @@ -1,44 +1,42 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 -# SPDX-FileCopyrightText: 2023 The Pion community -# SPDX-License-Identifier: MIT -"@roamhq/wrtc-darwin-arm64@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.8.0.tgz#15057e6b8f57e4d1b7008a9d848b6f2036adbb24" - integrity sha512-OtV2KWO7zOG3L8TF3KCt9aucynVCD/ww2xeXXgg+FLkya3ca0uzehN8EQJ3BL4tkInksbFJ2ssyu9cehfJ3ZuA== +"@roamhq/wrtc-darwin-arm64@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-arm64/-/wrtc-darwin-arm64-0.9.0.tgz#d3e304dd2d522953635bc372028abf81245e9ab6" + integrity sha512-bVbyI3Xm3ZlTPYnKz7gnLkxt1kZ8ZMW5QBwJpF/kS227O71pqUhicX/V5RRGqUmaER3JtXt0naR86ZPDfgTQBg== -"@roamhq/wrtc-darwin-x64@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.8.0.tgz#e09137b5a7edf2c2412bc63f1da1893dcaa211fd" - integrity sha512-VY7Vzt/SDDDCpW//h8GW9bOZrOr8gWXPZVD9473ypl4jyBIoO57yyLbHzd1G0vBUkS6szsHlQCz1WwpI30YL+g== +"@roamhq/wrtc-darwin-x64@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-darwin-x64/-/wrtc-darwin-x64-0.9.0.tgz#d879be0fe15f1e3dc9dcd29fb11a7edd73766992" + integrity sha512-99FFbFcIIqRR+H4ylkQgbgM8L5dx72hP5/GeJHB2h2f62Mb51bi30KokyOeBKH3X5R8bNi8LKnf7tV8d4kaGlA== -"@roamhq/wrtc-linux-arm64@0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.8.1.tgz#9a5f3297de44fcec86713d0baefa0594658ab71e" - integrity sha512-FBJLLazlWkGQUXaokC/rTbrUQbb0CNFYry52fZGstufrGLTWu+g4HcwXdVvxh1tnVtVMvkQGk+mlOL52sCxw0A== +"@roamhq/wrtc-linux-arm64@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-arm64/-/wrtc-linux-arm64-0.9.0.tgz#8a335f36c19ebcdf053e939c5cbfc29f40fe0df4" + integrity sha512-JUtrPFGvp+/5YWz9L5eW9e4XPQ5lRFQR5/BlGQJQOutrkBYqvME8NvAHKhyNWm+b3Sgk0w6yZ1g1nSttPHFY6g== -"@roamhq/wrtc-linux-x64@0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.8.1.tgz#3c5b60ca6cc6ebf5c2389d852f4a101135031da2" - integrity sha512-I9oWG7b4uvWO1IOR/aF34n+ID6TKVuSs0jd19h5KdhfRtw7FFh9xxuwN9rONPxLVa6fS0q+MCZgAf8Scz89L8Q== +"@roamhq/wrtc-linux-x64@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-linux-x64/-/wrtc-linux-x64-0.9.0.tgz#9de97d48bb4443cc25bbd18c523f02a706d9045c" + integrity sha512-ve6pIqeadUn6Jypb73A76041zdvGg/Bl0JU+T2+K8Zjw4aASebBFF4NX213dAS5viWUcNULzKV/s2PcpLDCyXQ== -"@roamhq/wrtc-win32-x64@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.8.0.tgz#582e2478df48201d5757b60dcd4b4dcc40a054b7" - integrity sha512-R2fxl41BLWPiP4eaTHGLzbbVvRjx1mV/OsgINCvawO7Hwz5Zx9I45+Fhrw3hd4n5amIeSG9VIF7Kz8eeTFXTGQ== +"@roamhq/wrtc-win32-x64@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc-win32-x64/-/wrtc-win32-x64-0.9.0.tgz#ad212e4305cc71084fab48d6d8c97a0e2360c813" + integrity sha512-QoVe5KKP1IjIRGDZfMUJV0OgTCK3noc79P2uuLeR5iJqf+bLcgyNr818ljB7frxPMSZNwWC5eVQTGmclrIIOIg== -"@roamhq/wrtc@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@roamhq/wrtc/-/wrtc-0.8.0.tgz#03c8c64c3b6a1e6e8965ec6496fa7e97571ae04b" - integrity sha512-C0V/nqc4/2xzORI5qa4mIeN/8UO3ywN1kInrJ9u6GljFx0D18JMUJEqe8yYHa61RrEeoWN3PKdW++k8TocSx/A== +"@roamhq/wrtc@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@roamhq/wrtc/-/wrtc-0.9.0.tgz#ef127439c16ef0723f234c56b06378c745fd6cc0" + integrity sha512-b61ld6g0hHDh/K1y0Lckxg5HBunaESNW6N23ewviW5IvwBfZiIYPUxQFWxqprMTr/6ODHmw5B2xvqvLyuAL5Xw== optionalDependencies: - "@roamhq/wrtc-darwin-arm64" "0.8.0" - "@roamhq/wrtc-darwin-x64" "0.8.0" - "@roamhq/wrtc-linux-arm64" "0.8.1" - "@roamhq/wrtc-linux-x64" "0.8.1" - "@roamhq/wrtc-win32-x64" "0.8.0" + "@roamhq/wrtc-darwin-arm64" "0.9.0" + "@roamhq/wrtc-darwin-x64" "0.9.0" + "@roamhq/wrtc-linux-arm64" "0.9.0" + "@roamhq/wrtc-linux-x64" "0.9.0" + "@roamhq/wrtc-win32-x64" "0.9.0" domexception "^4.0.0" ajv@^6.5.5: diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 2492f796a..d25979d9f 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -234,7 +234,7 @@ func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) { // Identifiers with the low five bits set indicate high-tag-number format // (two or more octets), which we don't support. if tag&0x1f == 0x1f { - b.err = fmt.Errorf("cryptobyte: high-tag number identifier octects not supported: 0x%x", tag) + b.err = fmt.Errorf("cryptobyte: high-tag number identifier octets not supported: 0x%x", tag) return } b.AddUint8(uint8(tag)) diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/net/proxy/per_host.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/net/proxy/per_host.go index d7d4b8b6e..32bdf435e 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/net/proxy/per_host.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/net/proxy/per_host.go @@ -7,6 +7,7 @@ package proxy import ( "context" "net" + "net/netip" "strings" ) @@ -57,7 +58,8 @@ func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net. } func (p *PerHost) dialerForRequest(host string) Dialer { - if ip := net.ParseIP(host); ip != nil { + if nip, err := netip.ParseAddr(host); err == nil { + ip := net.IP(nip.AsSlice()) for _, net := range p.bypassNetworks { if net.Contains(ip) { return p.bypass @@ -108,8 +110,8 @@ func (p *PerHost) AddFromString(s string) { } continue } - if ip := net.ParseIP(host); ip != nil { - p.AddIP(ip) + if nip, err := netip.ParseAddr(host); err == nil { + p.AddIP(net.IP(nip.AsSlice())) continue } if strings.HasPrefix(host, "*.") { diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu.go index 9c105f23a..63541994e 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu.go @@ -149,6 +149,18 @@ var ARM struct { _ CacheLinePad } +// The booleans in Loong64 contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +var Loong64 struct { + _ CacheLinePad + HasLSX bool // support 128-bit vector extension + HasLASX bool // support 256-bit vector extension + HasCRC32 bool // support CRC instruction + HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction + HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} instruction + _ CacheLinePad +} + // MIPS64X contains the supported CPU features of the current mips64/mips64le // platforms. If the current platform is not mips64/mips64le or the current // operating system is not Linux then all feature flags are false. @@ -220,6 +232,17 @@ var RISCV64 struct { HasZba bool // Address generation instructions extension HasZbb bool // Basic bit-manipulation extension HasZbs bool // Single-bit instructions extension + HasZvbb bool // Vector Basic Bit-manipulation + HasZvbc bool // Vector Carryless Multiplication + HasZvkb bool // Vector Cryptography Bit-manipulation + HasZvkt bool // Vector Data-Independent Execution Latency + HasZvkg bool // Vector GCM/GMAC + HasZvkn bool // NIST Algorithm Suite (AES/SHA256/SHA512) + HasZvknc bool // NIST Algorithm Suite with carryless multiply + HasZvkng bool // NIST Algorithm Suite with GCM + HasZvks bool // ShangMi Algorithm Suite + HasZvksc bool // ShangMi Algorithm Suite with carryless multiplication + HasZvksg bool // ShangMi Algorithm Suite with GCM _ CacheLinePad } diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go new file mode 100644 index 000000000..4f3411432 --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go @@ -0,0 +1,22 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +// HWCAP bits. These are exposed by the Linux kernel. +const ( + hwcap_LOONGARCH_LSX = 1 << 4 + hwcap_LOONGARCH_LASX = 1 << 5 +) + +func doinit() { + // TODO: Features that require kernel support like LSX and LASX can + // be detected here once needed in std library or by the compiler. + Loong64.HasLSX = hwcIsSet(hwCap, hwcap_LOONGARCH_LSX) + Loong64.HasLASX = hwcIsSet(hwCap, hwcap_LOONGARCH_LASX) +} + +func hwcIsSet(hwc uint, val uint) bool { + return hwc&val != 0 +} diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go index 7d902b684..a428dec9c 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 +//go:build linux && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 package cpu diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go index cb4a0c572..ad741536f 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_linux_riscv64.go @@ -58,6 +58,15 @@ const ( riscv_HWPROBE_EXT_ZBA = 0x8 riscv_HWPROBE_EXT_ZBB = 0x10 riscv_HWPROBE_EXT_ZBS = 0x20 + riscv_HWPROBE_EXT_ZVBB = 0x20000 + riscv_HWPROBE_EXT_ZVBC = 0x40000 + riscv_HWPROBE_EXT_ZVKB = 0x80000 + riscv_HWPROBE_EXT_ZVKG = 0x100000 + riscv_HWPROBE_EXT_ZVKNED = 0x200000 + riscv_HWPROBE_EXT_ZVKNHB = 0x800000 + riscv_HWPROBE_EXT_ZVKSED = 0x1000000 + riscv_HWPROBE_EXT_ZVKSH = 0x2000000 + riscv_HWPROBE_EXT_ZVKT = 0x4000000 riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 riscv_HWPROBE_MISALIGNED_FAST = 0x3 riscv_HWPROBE_MISALIGNED_MASK = 0x7 @@ -99,6 +108,20 @@ func doinit() { RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA) RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB) RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS) + RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB) + RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC) + RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB) + RISCV64.HasZvkg = isSet(v, riscv_HWPROBE_EXT_ZVKG) + RISCV64.HasZvkt = isSet(v, riscv_HWPROBE_EXT_ZVKT) + // Cryptography shorthand extensions + RISCV64.HasZvkn = isSet(v, riscv_HWPROBE_EXT_ZVKNED) && + isSet(v, riscv_HWPROBE_EXT_ZVKNHB) && RISCV64.HasZvkb && RISCV64.HasZvkt + RISCV64.HasZvknc = RISCV64.HasZvkn && RISCV64.HasZvbc + RISCV64.HasZvkng = RISCV64.HasZvkn && RISCV64.HasZvkg + RISCV64.HasZvks = isSet(v, riscv_HWPROBE_EXT_ZVKSED) && + isSet(v, riscv_HWPROBE_EXT_ZVKSH) && RISCV64.HasZvkb && RISCV64.HasZvkt + RISCV64.HasZvksc = RISCV64.HasZvks && RISCV64.HasZvbc + RISCV64.HasZvksg = RISCV64.HasZvks && RISCV64.HasZvkg } if pairs[1].key != -1 { v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.go index 558635850..45ecb29ae 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -8,5 +8,43 @@ package cpu const cacheLineSize = 64 +// Bit fields for CPUCFG registers, Related reference documents: +// https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_cpucfg +const ( + // CPUCFG1 bits + cpucfg1_CRC32 = 1 << 25 + + // CPUCFG2 bits + cpucfg2_LAM_BH = 1 << 27 + cpucfg2_LAMCAS = 1 << 28 +) + func initOptions() { + options = []option{ + {Name: "lsx", Feature: &Loong64.HasLSX}, + {Name: "lasx", Feature: &Loong64.HasLASX}, + {Name: "crc32", Feature: &Loong64.HasCRC32}, + {Name: "lam_bh", Feature: &Loong64.HasLAM_BH}, + {Name: "lamcas", Feature: &Loong64.HasLAMCAS}, + } + + // The CPUCFG data on Loong64 only reflects the hardware capabilities, + // not the kernel support status, so features such as LSX and LASX that + // require kernel support cannot be obtained from the CPUCFG data. + // + // These features only require hardware capability support and do not + // require kernel specific support, so they can be obtained directly + // through CPUCFG + cfg1 := get_cpucfg(1) + cfg2 := get_cpucfg(2) + + Loong64.HasCRC32 = cfgIsSet(cfg1, cpucfg1_CRC32) + Loong64.HasLAMCAS = cfgIsSet(cfg2, cpucfg2_LAMCAS) + Loong64.HasLAM_BH = cfgIsSet(cfg2, cpucfg2_LAM_BH) +} + +func get_cpucfg(reg uint32) uint32 + +func cfgIsSet(cfg uint32, val uint32) bool { + return cfg&val != 0 } diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.s new file mode 100644 index 000000000..71cbaf1ce --- /dev/null +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_loong64.s @@ -0,0 +1,13 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func get_cpucfg(reg uint32) uint32 +TEXT ·get_cpucfg(SB), NOSPLIT|NOFRAME, $0 + MOVW reg+0(FP), R5 + // CPUCFG R5, R4 = 0x00006ca4 + WORD $0x00006ca4 + MOVW R4, ret+8(FP) + RET diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_riscv64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_riscv64.go index aca3199c9..0f617aef5 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_riscv64.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/cpu_riscv64.go @@ -16,5 +16,17 @@ func initOptions() { {Name: "zba", Feature: &RISCV64.HasZba}, {Name: "zbb", Feature: &RISCV64.HasZbb}, {Name: "zbs", Feature: &RISCV64.HasZbs}, + // RISC-V Cryptography Extensions + {Name: "zvbb", Feature: &RISCV64.HasZvbb}, + {Name: "zvbc", Feature: &RISCV64.HasZvbc}, + {Name: "zvkb", Feature: &RISCV64.HasZvkb}, + {Name: "zvkg", Feature: &RISCV64.HasZvkg}, + {Name: "zvkt", Feature: &RISCV64.HasZvkt}, + {Name: "zvkn", Feature: &RISCV64.HasZvkn}, + {Name: "zvknc", Feature: &RISCV64.HasZvknc}, + {Name: "zvkng", Feature: &RISCV64.HasZvkng}, + {Name: "zvks", Feature: &RISCV64.HasZvks}, + {Name: "zvksc", Feature: &RISCV64.HasZvksc}, + {Name: "zvksg", Feature: &RISCV64.HasZvksg}, } } diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/parse.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/parse.go index 762b63d68..56a7e1a17 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/parse.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/cpu/parse.go @@ -13,7 +13,7 @@ import "strconv" // https://golang.org/cl/209597. func parseRelease(rel string) (major, minor, patch int, ok bool) { // Strip anything after a dash or plus. - for i := 0; i < len(rel); i++ { + for i := range len(rel) { if rel[i] == '-' || rel[i] == '+' { rel = rel[:i] break @@ -21,7 +21,7 @@ func parseRelease(rel string) (major, minor, patch int, ok bool) { } next := func() (int, bool) { - for i := 0; i < len(rel); i++ { + for i := range len(rel) { if rel[i] == '.' { ver, err := strconv.Atoi(rel[:i]) rel = rel[i+1:] diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.go index 099867dee..798f61ad3 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -602,7 +602,150 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI return } -//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) +// sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) +const minIovec = 8 + +func Readv(fd int, iovs [][]byte) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + n, err = readv(fd, iovecs) + readvRacedetect(iovecs, n, err) + return n, err +} + +func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + n, err = preadv(fd, iovecs, offset) + readvRacedetect(iovecs, n, err) + return n, err +} + +func Writev(fd int, iovs [][]byte) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = writev(fd, iovecs) + writevRacedetect(iovecs, n) + return n, err +} + +func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = pwritev(fd, iovecs, offset) + writevRacedetect(iovecs, n) + return n, err +} + +func appendBytes(vecs []Iovec, bs [][]byte) []Iovec { + for _, b := range bs { + var v Iovec + v.SetLen(len(b)) + if len(b) > 0 { + v.Base = &b[0] + } else { + v.Base = (*byte)(unsafe.Pointer(&_zero)) + } + vecs = append(vecs, v) + } + return vecs +} + +func writevRacedetect(iovecs []Iovec, n int) { + if !raceenabled { + return + } + for i := 0; n > 0 && i < len(iovecs); i++ { + m := int(iovecs[i].Len) + if m > n { + m = n + } + n -= m + if m > 0 { + raceReadRange(unsafe.Pointer(iovecs[i].Base), m) + } + } +} + +func readvRacedetect(iovecs []Iovec, n int, err error) { + if !raceenabled { + return + } + for i := 0; n > 0 && i < len(iovecs); i++ { + m := int(iovecs[i].Len) + if m > n { + m = n + } + n -= m + if m > 0 { + raceWriteRange(unsafe.Pointer(iovecs[i].Base), m) + } + } + if err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } +} + +func darwinMajorMinPatch() (maj, min, patch int, err error) { + var un Utsname + err = Uname(&un) + if err != nil { + return + } + + var mmp [3]int + c := 0 +Loop: + for _, b := range un.Release[:] { + switch { + case b >= '0' && b <= '9': + mmp[c] = 10*mmp[c] + int(b-'0') + case b == '.': + c++ + if c > 2 { + return 0, 0, 0, ENOTSUP + } + case b == 0: + break Loop + default: + return 0, 0, 0, ENOTSUP + } + } + if c != 2 { + return 0, 0, 0, ENOTSUP + } + return mmp[0], mmp[1], mmp[2], nil +} + +func darwinKernelVersionMin(maj, min, patch int) bool { + actualMaj, actualMin, actualPatch, err := darwinMajorMinPatch() + if err != nil { + return false + } + return actualMaj > maj || actualMaj == maj && (actualMin > min || actualMin == min && actualPatch >= patch) +} + //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) @@ -705,3 +848,7 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI //sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) +//sys readv(fd int, iovecs []Iovec) (n int, err error) +//sys preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) +//sys writev(fd int, iovecs []Iovec) (n int, err error) +//sys pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go index 230a94549..4958a6570 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -13,6 +13,7 @@ package unix import ( "encoding/binary" + "slices" "strconv" "syscall" "time" @@ -417,7 +418,7 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { return nil, 0, EINVAL } sa.raw.Family = AF_UNIX - for i := 0; i < n; i++ { + for i := range n { sa.raw.Path[i] = int8(name[i]) } // length is family (uint16), name, NUL. @@ -507,7 +508,7 @@ func (sa *SockaddrL2) sockaddr() (unsafe.Pointer, _Socklen, error) { psm := (*[2]byte)(unsafe.Pointer(&sa.raw.Psm)) psm[0] = byte(sa.PSM) psm[1] = byte(sa.PSM >> 8) - for i := 0; i < len(sa.Addr); i++ { + for i := range len(sa.Addr) { sa.raw.Bdaddr[i] = sa.Addr[len(sa.Addr)-1-i] } cid := (*[2]byte)(unsafe.Pointer(&sa.raw.Cid)) @@ -589,11 +590,11 @@ func (sa *SockaddrCAN) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Family = AF_CAN sa.raw.Ifindex = int32(sa.Ifindex) rx := (*[4]byte)(unsafe.Pointer(&sa.RxID)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i] = rx[i] } tx := (*[4]byte)(unsafe.Pointer(&sa.TxID)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i+4] = tx[i] } return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil @@ -618,11 +619,11 @@ func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Family = AF_CAN sa.raw.Ifindex = int32(sa.Ifindex) n := (*[8]byte)(unsafe.Pointer(&sa.Name)) - for i := 0; i < 8; i++ { + for i := range 8 { sa.raw.Addr[i] = n[i] } p := (*[4]byte)(unsafe.Pointer(&sa.PGN)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i+8] = p[i] } sa.raw.Addr[12] = sa.Addr @@ -911,7 +912,7 @@ func (sa *SockaddrIUCV) sockaddr() (unsafe.Pointer, _Socklen, error) { // These are EBCDIC encoded by the kernel, but we still need to pad them // with blanks. Initializing with blanks allows the caller to feed in either // a padded or an unpadded string. - for i := 0; i < 8; i++ { + for i := range 8 { sa.raw.Nodeid[i] = ' ' sa.raw.User_id[i] = ' ' sa.raw.Name[i] = ' ' @@ -1148,7 +1149,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { var user [8]byte var name [8]byte - for i := 0; i < 8; i++ { + for i := range 8 { user[i] = byte(pp.User_id[i]) name[i] = byte(pp.Name[i]) } @@ -1173,11 +1174,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { Ifindex: int(pp.Ifindex), } name := (*[8]byte)(unsafe.Pointer(&sa.Name)) - for i := 0; i < 8; i++ { + for i := range 8 { name[i] = pp.Addr[i] } pgn := (*[4]byte)(unsafe.Pointer(&sa.PGN)) - for i := 0; i < 4; i++ { + for i := range 4 { pgn[i] = pp.Addr[i+8] } addr := (*[1]byte)(unsafe.Pointer(&sa.Addr)) @@ -1188,11 +1189,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { Ifindex: int(pp.Ifindex), } rx := (*[4]byte)(unsafe.Pointer(&sa.RxID)) - for i := 0; i < 4; i++ { + for i := range 4 { rx[i] = pp.Addr[i] } tx := (*[4]byte)(unsafe.Pointer(&sa.TxID)) - for i := 0; i < 4; i++ { + for i := range 4 { tx[i] = pp.Addr[i+4] } return sa, nil @@ -2216,10 +2217,7 @@ func readvRacedetect(iovecs []Iovec, n int, err error) { return } for i := 0; n > 0 && i < len(iovecs); i++ { - m := int(iovecs[i].Len) - if m > n { - m = n - } + m := min(int(iovecs[i].Len), n) n -= m if m > 0 { raceWriteRange(unsafe.Pointer(iovecs[i].Base), m) @@ -2270,10 +2268,7 @@ func writevRacedetect(iovecs []Iovec, n int) { return } for i := 0; n > 0 && i < len(iovecs); i++ { - m := int(iovecs[i].Len) - if m > n { - m = n - } + m := min(int(iovecs[i].Len), n) n -= m if m > 0 { raceReadRange(unsafe.Pointer(iovecs[i].Base), m) @@ -2320,12 +2315,7 @@ func isGroupMember(gid int) bool { return false } - for _, g := range groups { - if g == gid { - return true - } - } - return false + return slices.Contains(groups, gid) } func isCapDacOverrideSet() bool { diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 24b346e1a..813c05b66 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func readv(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_readv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_preadv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writev(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_writev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pwritev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := syscall_syscall(libc_fstat64_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index ebd213100..fda328582 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_readv(SB) +GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB) + +TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_preadv(SB) +GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB) + +TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_writev(SB) +GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB) + +TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pwritev(SB) +GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB) + TEXT libc_fstat64_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fstat64(SB) GLOBL ·libc_fstat64_trampoline_addr(SB), RODATA, $8 diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 824b9c2d5..e6f58f3c6 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func readv(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_readv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_preadv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writev(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_writev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pwritev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := syscall_syscall(libc_fstat_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 4f178a229..7f8998b90 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_readv(SB) +GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB) + +TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_preadv(SB) +GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB) + +TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_writev(SB) +GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB) + +TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pwritev(SB) +GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB) + TEXT libc_fstat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fstat(SB) GLOBL ·libc_fstat_trampoline_addr(SB), RODATA, $8 diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go index b6e1ab76f..a8b0364c7 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/security_windows.go @@ -1303,7 +1303,10 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE return nil, err } if absoluteSDSize > 0 { - absoluteSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, absoluteSDSize)[0])) + absoluteSD = new(SECURITY_DESCRIPTOR) + if unsafe.Sizeof(*absoluteSD) < uintptr(absoluteSDSize) { + panic("sizeof(SECURITY_DESCRIPTOR) too small") + } } var ( dacl *ACL @@ -1312,19 +1315,55 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE group *SID ) if daclSize > 0 { - dacl = (*ACL)(unsafe.Pointer(&make([]byte, daclSize)[0])) + dacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, daclSize)))) } if saclSize > 0 { - sacl = (*ACL)(unsafe.Pointer(&make([]byte, saclSize)[0])) + sacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, saclSize)))) } if ownerSize > 0 { - owner = (*SID)(unsafe.Pointer(&make([]byte, ownerSize)[0])) + owner = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, ownerSize)))) } if groupSize > 0 { - group = (*SID)(unsafe.Pointer(&make([]byte, groupSize)[0])) + group = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, groupSize)))) } + // We call into Windows via makeAbsoluteSD, which sets up + // pointers within absoluteSD that point to other chunks of memory + // we pass into makeAbsoluteSD, and that happens outside the view of the GC. + // We therefore take some care here to then verify the pointers are as we expect + // and set them explicitly in view of the GC. See https://go.dev/issue/73199. + // TODO: consider weak pointers once Go 1.24 is appropriate. See suggestion in https://go.dev/cl/663575. err = makeAbsoluteSD(selfRelativeSD, absoluteSD, &absoluteSDSize, dacl, &daclSize, sacl, &saclSize, owner, &ownerSize, group, &groupSize) + if err != nil { + // Don't return absoluteSD, which might be partially initialized. + return nil, err + } + // Before using any fields, verify absoluteSD is in the format we expect according to Windows. + // See https://learn.microsoft.com/en-us/windows/win32/secauthz/absolute-and-self-relative-security-descriptors + absControl, _, err := absoluteSD.Control() + if err != nil { + panic("absoluteSD: " + err.Error()) + } + if absControl&SE_SELF_RELATIVE != 0 { + panic("absoluteSD not in absolute format") + } + if absoluteSD.dacl != dacl { + panic("dacl pointer mismatch") + } + if absoluteSD.sacl != sacl { + panic("sacl pointer mismatch") + } + if absoluteSD.owner != owner { + panic("owner pointer mismatch") + } + if absoluteSD.group != group { + panic("group pointer mismatch") + } + absoluteSD.dacl = dacl + absoluteSD.sacl = sacl + absoluteSD.owner = owner + absoluteSD.group = group + return } diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go index 4a3254386..640f6b153 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -870,6 +870,7 @@ const socket_error = uintptr(^uint32(0)) //sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecvFrom //sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSASendTo //sys WSASocket(af int32, typ int32, protocol int32, protoInfo *WSAProtocolInfo, group uint32, flags uint32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.WSASocketW +//sys WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err error) [failretval!=0] = ws2_32.WSADuplicateSocketW //sys GetHostByName(name string) (h *Hostent, err error) [failretval==nil] = ws2_32.gethostbyname //sys GetServByName(name string, proto string) (s *Servent, err error) [failretval==nil] = ws2_32.getservbyname //sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs @@ -1698,8 +1699,9 @@ func NewNTUnicodeString(s string) (*NTUnicodeString, error) { // Slice returns a uint16 slice that aliases the data in the NTUnicodeString. func (s *NTUnicodeString) Slice() []uint16 { - slice := unsafe.Slice(s.Buffer, s.MaximumLength) - return slice[:s.Length] + // Note: this rounds the length down, if it happens + // to (incorrectly) be odd. Probably safer than rounding up. + return unsafe.Slice(s.Buffer, s.MaximumLength/2)[:s.Length/2] } func (s *NTUnicodeString) String() string { diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go index 9d138de5f..958bcf47a 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/types_windows.go @@ -1074,6 +1074,7 @@ const ( IP_ADD_MEMBERSHIP = 0xc IP_DROP_MEMBERSHIP = 0xd IP_PKTINFO = 0x13 + IP_MTU_DISCOVER = 0x47 IPV6_V6ONLY = 0x1b IPV6_UNICAST_HOPS = 0x4 @@ -1083,6 +1084,7 @@ const ( IPV6_JOIN_GROUP = 0xc IPV6_LEAVE_GROUP = 0xd IPV6_PKTINFO = 0x13 + IPV6_MTU_DISCOVER = 0x47 MSG_OOB = 0x1 MSG_PEEK = 0x2 @@ -1132,6 +1134,15 @@ const ( WSASYS_STATUS_LEN = 128 ) +// enum PMTUD_STATE from ws2ipdef.h +const ( + IP_PMTUDISC_NOT_SET = 0 + IP_PMTUDISC_DO = 1 + IP_PMTUDISC_DONT = 2 + IP_PMTUDISC_PROBE = 3 + IP_PMTUDISC_MAX = 4 +) + type WSABuf struct { Len uint32 Buf *byte @@ -1146,6 +1157,22 @@ type WSAMsg struct { Flags uint32 } +type WSACMSGHDR struct { + Len uintptr + Level int32 + Type int32 +} + +type IN_PKTINFO struct { + Addr [4]byte + Ifindex uint32 +} + +type IN6_PKTINFO struct { + Addr [16]byte + Ifindex uint32 +} + // Flags for WSASocket const ( WSA_FLAG_OVERLAPPED = 0x01 @@ -2673,6 +2700,8 @@ type CommTimeouts struct { // NTUnicodeString is a UTF-16 string for NT native APIs, corresponding to UNICODE_STRING. type NTUnicodeString struct { + // Note: Length and MaximumLength are in *bytes*, not uint16s. + // They should always be even. Length uint16 MaximumLength uint16 Buffer *uint16 @@ -3601,3 +3630,213 @@ const ( KLF_NOTELLSHELL = 0x00000080 KLF_SETFORPROCESS = 0x00000100 ) + +// Virtual Key codes +// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes +const ( + VK_LBUTTON = 0x01 + VK_RBUTTON = 0x02 + VK_CANCEL = 0x03 + VK_MBUTTON = 0x04 + VK_XBUTTON1 = 0x05 + VK_XBUTTON2 = 0x06 + VK_BACK = 0x08 + VK_TAB = 0x09 + VK_CLEAR = 0x0C + VK_RETURN = 0x0D + VK_SHIFT = 0x10 + VK_CONTROL = 0x11 + VK_MENU = 0x12 + VK_PAUSE = 0x13 + VK_CAPITAL = 0x14 + VK_KANA = 0x15 + VK_HANGEUL = 0x15 + VK_HANGUL = 0x15 + VK_IME_ON = 0x16 + VK_JUNJA = 0x17 + VK_FINAL = 0x18 + VK_HANJA = 0x19 + VK_KANJI = 0x19 + VK_IME_OFF = 0x1A + VK_ESCAPE = 0x1B + VK_CONVERT = 0x1C + VK_NONCONVERT = 0x1D + VK_ACCEPT = 0x1E + VK_MODECHANGE = 0x1F + VK_SPACE = 0x20 + VK_PRIOR = 0x21 + VK_NEXT = 0x22 + VK_END = 0x23 + VK_HOME = 0x24 + VK_LEFT = 0x25 + VK_UP = 0x26 + VK_RIGHT = 0x27 + VK_DOWN = 0x28 + VK_SELECT = 0x29 + VK_PRINT = 0x2A + VK_EXECUTE = 0x2B + VK_SNAPSHOT = 0x2C + VK_INSERT = 0x2D + VK_DELETE = 0x2E + VK_HELP = 0x2F + VK_LWIN = 0x5B + VK_RWIN = 0x5C + VK_APPS = 0x5D + VK_SLEEP = 0x5F + VK_NUMPAD0 = 0x60 + VK_NUMPAD1 = 0x61 + VK_NUMPAD2 = 0x62 + VK_NUMPAD3 = 0x63 + VK_NUMPAD4 = 0x64 + VK_NUMPAD5 = 0x65 + VK_NUMPAD6 = 0x66 + VK_NUMPAD7 = 0x67 + VK_NUMPAD8 = 0x68 + VK_NUMPAD9 = 0x69 + VK_MULTIPLY = 0x6A + VK_ADD = 0x6B + VK_SEPARATOR = 0x6C + VK_SUBTRACT = 0x6D + VK_DECIMAL = 0x6E + VK_DIVIDE = 0x6F + VK_F1 = 0x70 + VK_F2 = 0x71 + VK_F3 = 0x72 + VK_F4 = 0x73 + VK_F5 = 0x74 + VK_F6 = 0x75 + VK_F7 = 0x76 + VK_F8 = 0x77 + VK_F9 = 0x78 + VK_F10 = 0x79 + VK_F11 = 0x7A + VK_F12 = 0x7B + VK_F13 = 0x7C + VK_F14 = 0x7D + VK_F15 = 0x7E + VK_F16 = 0x7F + VK_F17 = 0x80 + VK_F18 = 0x81 + VK_F19 = 0x82 + VK_F20 = 0x83 + VK_F21 = 0x84 + VK_F22 = 0x85 + VK_F23 = 0x86 + VK_F24 = 0x87 + VK_NUMLOCK = 0x90 + VK_SCROLL = 0x91 + VK_OEM_NEC_EQUAL = 0x92 + VK_OEM_FJ_JISHO = 0x92 + VK_OEM_FJ_MASSHOU = 0x93 + VK_OEM_FJ_TOUROKU = 0x94 + VK_OEM_FJ_LOYA = 0x95 + VK_OEM_FJ_ROYA = 0x96 + VK_LSHIFT = 0xA0 + VK_RSHIFT = 0xA1 + VK_LCONTROL = 0xA2 + VK_RCONTROL = 0xA3 + VK_LMENU = 0xA4 + VK_RMENU = 0xA5 + VK_BROWSER_BACK = 0xA6 + VK_BROWSER_FORWARD = 0xA7 + VK_BROWSER_REFRESH = 0xA8 + VK_BROWSER_STOP = 0xA9 + VK_BROWSER_SEARCH = 0xAA + VK_BROWSER_FAVORITES = 0xAB + VK_BROWSER_HOME = 0xAC + VK_VOLUME_MUTE = 0xAD + VK_VOLUME_DOWN = 0xAE + VK_VOLUME_UP = 0xAF + VK_MEDIA_NEXT_TRACK = 0xB0 + VK_MEDIA_PREV_TRACK = 0xB1 + VK_MEDIA_STOP = 0xB2 + VK_MEDIA_PLAY_PAUSE = 0xB3 + VK_LAUNCH_MAIL = 0xB4 + VK_LAUNCH_MEDIA_SELECT = 0xB5 + VK_LAUNCH_APP1 = 0xB6 + VK_LAUNCH_APP2 = 0xB7 + VK_OEM_1 = 0xBA + VK_OEM_PLUS = 0xBB + VK_OEM_COMMA = 0xBC + VK_OEM_MINUS = 0xBD + VK_OEM_PERIOD = 0xBE + VK_OEM_2 = 0xBF + VK_OEM_3 = 0xC0 + VK_OEM_4 = 0xDB + VK_OEM_5 = 0xDC + VK_OEM_6 = 0xDD + VK_OEM_7 = 0xDE + VK_OEM_8 = 0xDF + VK_OEM_AX = 0xE1 + VK_OEM_102 = 0xE2 + VK_ICO_HELP = 0xE3 + VK_ICO_00 = 0xE4 + VK_PROCESSKEY = 0xE5 + VK_ICO_CLEAR = 0xE6 + VK_OEM_RESET = 0xE9 + VK_OEM_JUMP = 0xEA + VK_OEM_PA1 = 0xEB + VK_OEM_PA2 = 0xEC + VK_OEM_PA3 = 0xED + VK_OEM_WSCTRL = 0xEE + VK_OEM_CUSEL = 0xEF + VK_OEM_ATTN = 0xF0 + VK_OEM_FINISH = 0xF1 + VK_OEM_COPY = 0xF2 + VK_OEM_AUTO = 0xF3 + VK_OEM_ENLW = 0xF4 + VK_OEM_BACKTAB = 0xF5 + VK_ATTN = 0xF6 + VK_CRSEL = 0xF7 + VK_EXSEL = 0xF8 + VK_EREOF = 0xF9 + VK_PLAY = 0xFA + VK_ZOOM = 0xFB + VK_NONAME = 0xFC + VK_PA1 = 0xFD + VK_OEM_CLEAR = 0xFE +) + +// Mouse button constants. +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str +const ( + FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001 + RIGHTMOST_BUTTON_PRESSED = 0x0002 + FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004 + FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008 + FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010 +) + +// Control key state constaints. +// https://docs.microsoft.com/en-us/windows/console/key-event-record-str +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str +const ( + CAPSLOCK_ON = 0x0080 + ENHANCED_KEY = 0x0100 + LEFT_ALT_PRESSED = 0x0002 + LEFT_CTRL_PRESSED = 0x0008 + NUMLOCK_ON = 0x0020 + RIGHT_ALT_PRESSED = 0x0001 + RIGHT_CTRL_PRESSED = 0x0004 + SCROLLLOCK_ON = 0x0040 + SHIFT_PRESSED = 0x0010 +) + +// Mouse event record event flags. +// https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str +const ( + MOUSE_MOVED = 0x0001 + DOUBLE_CLICK = 0x0002 + MOUSE_WHEELED = 0x0004 + MOUSE_HWHEELED = 0x0008 +) + +// Input Record Event Types +// https://learn.microsoft.com/en-us/windows/console/input-record-str +const ( + FOCUS_EVENT = 0x0010 + KEY_EVENT = 0x0001 + MENU_EVENT = 0x0008 + MOUSE_EVENT = 0x0002 + WINDOW_BUFFER_SIZE_EVENT = 0x0004 +) diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 01c0716c2..a58bc48b8 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -511,6 +511,7 @@ var ( procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") procWSACleanup = modws2_32.NewProc("WSACleanup") + procWSADuplicateSocketW = modws2_32.NewProc("WSADuplicateSocketW") procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW") procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") procWSAIoctl = modws2_32.NewProc("WSAIoctl") @@ -4391,6 +4392,14 @@ func WSACleanup() (err error) { return } +func WSADuplicateSocket(s Handle, processID uint32, info *WSAProtocolInfo) (err error) { + r1, _, e1 := syscall.Syscall(procWSADuplicateSocketW.Addr(), 3, uintptr(s), uintptr(processID), uintptr(unsafe.Pointer(info))) + if r1 != 0 { + err = errnoErr(e1) + } + return +} + func WSAEnumProtocols(protocols *int32, protocolBuffer *WSAProtocolInfo, bufferLength *uint32) (n int32, err error) { r0, _, e1 := syscall.Syscall(procWSAEnumProtocolsW.Addr(), 3, uintptr(unsafe.Pointer(protocols)), uintptr(unsafe.Pointer(protocolBuffer)), uintptr(unsafe.Pointer(bufferLength))) n = int32(r0) diff --git a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go index f636667fb..13e9a64ad 100644 --- a/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go +++ b/trunk/3rdparty/srs-bench/vendor/golang.org/x/term/terminal.go @@ -6,6 +6,7 @@ package term import ( "bytes" + "fmt" "io" "runtime" "strconv" @@ -36,6 +37,26 @@ var vt100EscapeCodes = EscapeCodes{ Reset: []byte{keyEscape, '[', '0', 'm'}, } +// A History provides a (possibly bounded) queue of input lines read by [Terminal.ReadLine]. +type History interface { + // Add will be called by [Terminal.ReadLine] to add + // a new, most recent entry to the history. + // It is allowed to drop any entry, including + // the entry being added (e.g., if it's deemed an invalid entry), + // the least-recent entry (e.g., to keep the history bounded), + // or any other entry. + Add(entry string) + + // Len returns the number of entries in the history. + Len() int + + // At returns an entry from the history. + // Index 0 is the most-recently added entry and + // index Len()-1 is the least-recently added entry. + // If index is < 0 or >= Len(), it panics. + At(idx int) string +} + // Terminal contains the state for running a VT100 terminal that is capable of // reading lines of input. type Terminal struct { @@ -44,6 +65,8 @@ type Terminal struct { // bytes, as an index into |line|). If it returns ok=false, the key // press is processed normally. Otherwise it returns a replacement line // and the new cursor position. + // + // This will be disabled during ReadPassword. AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) // Escape contains a pointer to the escape codes for this terminal. @@ -84,9 +107,14 @@ type Terminal struct { remainder []byte inBuf [256]byte - // history contains previously entered commands so that they can be - // accessed with the up and down keys. - history stRingBuffer + // History records and retrieves lines of input read by [ReadLine] which + // a user can retrieve and navigate using the up and down arrow keys. + // + // It is not safe to call ReadLine concurrently with any methods on History. + // + // [NewTerminal] sets this to a default implementation that records the + // last 100 lines of input. + History History // historyIndex stores the currently accessed history entry, where zero // means the immediately previous entry. historyIndex int @@ -109,6 +137,7 @@ func NewTerminal(c io.ReadWriter, prompt string) *Terminal { termHeight: 24, echo: true, historyIndex: -1, + History: &stRingBuffer{}, } } @@ -448,6 +477,23 @@ func visualLength(runes []rune) int { return length } +// histroryAt unlocks the terminal and relocks it while calling History.At. +func (t *Terminal) historyAt(idx int) (string, bool) { + t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer. + defer t.lock.Lock() // panic in At (or Len) protection. + if idx < 0 || idx >= t.History.Len() { + return "", false + } + return t.History.At(idx), true +} + +// historyAdd unlocks the terminal and relocks it while calling History.Add. +func (t *Terminal) historyAdd(entry string) { + t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer. + defer t.lock.Lock() // panic in Add protection. + t.History.Add(entry) +} + // handleKey processes the given key and, optionally, returns a line of text // that the user has entered. func (t *Terminal) handleKey(key rune) (line string, ok bool) { @@ -495,7 +541,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { t.pos = len(t.line) t.moveCursorToPos(t.pos) case keyUp: - entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1) + entry, ok := t.historyAt(t.historyIndex + 1) if !ok { return "", false } @@ -514,7 +560,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) { t.setLine(runes, len(runes)) t.historyIndex-- default: - entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1) + entry, ok := t.historyAt(t.historyIndex - 1) if ok { t.historyIndex-- runes := []rune(entry) @@ -692,6 +738,8 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { // ReadPassword temporarily changes the prompt and reads a password, without // echo, from the terminal. +// +// The AutoCompleteCallback is disabled during this call. func (t *Terminal) ReadPassword(prompt string) (line string, err error) { t.lock.Lock() defer t.lock.Unlock() @@ -699,6 +747,11 @@ func (t *Terminal) ReadPassword(prompt string) (line string, err error) { oldPrompt := t.prompt t.prompt = []rune(prompt) t.echo = false + oldAutoCompleteCallback := t.AutoCompleteCallback + t.AutoCompleteCallback = nil + defer func() { + t.AutoCompleteCallback = oldAutoCompleteCallback + }() line, err = t.readLine() @@ -772,7 +825,7 @@ func (t *Terminal) readLine() (line string, err error) { if lineOk { if t.echo { t.historyIndex = -1 - t.history.Add(line) + t.historyAdd(line) } if lineIsPasted { err = ErrPasteIndicator @@ -929,19 +982,23 @@ func (s *stRingBuffer) Add(a string) { } } -// NthPreviousEntry returns the value passed to the nth previous call to Add. +func (s *stRingBuffer) Len() int { + return s.size +} + +// At returns the value passed to the nth previous call to Add. // If n is zero then the immediately prior value is returned, if one, then the // next most recent, and so on. If such an element doesn't exist then ok is // false. -func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { +func (s *stRingBuffer) At(n int) string { if n < 0 || n >= s.size { - return "", false + panic(fmt.Sprintf("term: history index [%d] out of range [0,%d)", n, s.size)) } index := s.head - n if index < 0 { index += s.max } - return s.entries[index], true + return s.entries[index] } // readPasswordLine reads from reader until it finds \n or io.EOF. diff --git a/trunk/3rdparty/srs-bench/vendor/modules.txt b/trunk/3rdparty/srs-bench/vendor/modules.txt index a04d82823..2c6673217 100644 --- a/trunk/3rdparty/srs-bench/vendor/modules.txt +++ b/trunk/3rdparty/srs-bench/vendor/modules.txt @@ -89,16 +89,19 @@ github.com/pion/ice/v4/internal/atomic github.com/pion/ice/v4/internal/fakenet github.com/pion/ice/v4/internal/stun github.com/pion/ice/v4/internal/taskloop -# github.com/pion/interceptor v0.1.37 +# github.com/pion/interceptor v0.1.40 ## explicit; go 1.20 github.com/pion/interceptor github.com/pion/interceptor/internal/ntp +github.com/pion/interceptor/internal/rtpbuffer github.com/pion/interceptor/internal/sequencenumber +github.com/pion/interceptor/pkg/flexfec +github.com/pion/interceptor/pkg/flexfec/util github.com/pion/interceptor/pkg/nack github.com/pion/interceptor/pkg/report github.com/pion/interceptor/pkg/rfc8888 github.com/pion/interceptor/pkg/twcc -# github.com/pion/logging v0.2.3 +# github.com/pion/logging v0.2.4 ## explicit; go 1.20 github.com/pion/logging # github.com/pion/mdns/v2 v2.0.7 @@ -110,7 +113,7 @@ github.com/pion/randutil # github.com/pion/rtcp v1.2.15 ## explicit; go 1.20 github.com/pion/rtcp -# github.com/pion/rtp v1.8.15 +# github.com/pion/rtp v1.8.20 ## explicit; go 1.20 github.com/pion/rtp github.com/pion/rtp/codecs @@ -119,10 +122,10 @@ github.com/pion/rtp/codecs/vp9 # github.com/pion/sctp v1.8.39 ## explicit; go 1.20 github.com/pion/sctp -# github.com/pion/sdp/v3 v3.0.11 +# github.com/pion/sdp/v3 v3.0.14 ## explicit; go 1.20 github.com/pion/sdp/v3 -# github.com/pion/srtp/v3 v3.0.4 +# github.com/pion/srtp/v3 v3.0.6 ## explicit; go 1.20 github.com/pion/srtp/v3 # github.com/pion/stun/v3 v3.0.0 @@ -139,15 +142,15 @@ github.com/pion/transport/v3/replaydetector github.com/pion/transport/v3/stdnet github.com/pion/transport/v3/utils/xor github.com/pion/transport/v3/vnet -# github.com/pion/turn/v4 v4.0.0 -## explicit; go 1.19 +# github.com/pion/turn/v4 v4.0.2 +## explicit; go 1.20 github.com/pion/turn/v4 github.com/pion/turn/v4/internal/allocation github.com/pion/turn/v4/internal/client github.com/pion/turn/v4/internal/ipnet github.com/pion/turn/v4/internal/proto github.com/pion/turn/v4/internal/server -# github.com/pion/webrtc/v4 v4.1.1 +# github.com/pion/webrtc/v4 v4.1.3 ## explicit; go 1.20 github.com/pion/webrtc/v4 github.com/pion/webrtc/v4/internal/fmtp @@ -156,6 +159,7 @@ github.com/pion/webrtc/v4/internal/util github.com/pion/webrtc/v4/pkg/media github.com/pion/webrtc/v4/pkg/media/h264reader github.com/pion/webrtc/v4/pkg/media/h264writer +github.com/pion/webrtc/v4/pkg/media/h265reader github.com/pion/webrtc/v4/pkg/media/ivfwriter github.com/pion/webrtc/v4/pkg/media/oggreader github.com/pion/webrtc/v4/pkg/media/oggwriter @@ -184,14 +188,14 @@ github.com/yapingcat/gomedia/codec # github.com/yapingcat/gomedia/mpeg2 v0.0.0-20220617074658-94762898dc25 ## explicit; go 1.16 github.com/yapingcat/gomedia/mpeg2 -# golang.org/x/crypto v0.33.0 -## explicit; go 1.20 +# golang.org/x/crypto v0.39.0 +## explicit; go 1.23.0 golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/curve25519 golang.org/x/crypto/ssh/terminal -# golang.org/x/net v0.35.0 -## explicit; go 1.18 +# golang.org/x/net v0.41.0 +## explicit; go 1.23.0 golang.org/x/net/bpf golang.org/x/net/dns/dnsmessage golang.org/x/net/internal/iana @@ -200,12 +204,12 @@ golang.org/x/net/internal/socks golang.org/x/net/ipv4 golang.org/x/net/ipv6 golang.org/x/net/proxy -# golang.org/x/sys v0.30.0 -## explicit; go 1.18 +# golang.org/x/sys v0.33.0 +## explicit; go 1.23.0 golang.org/x/sys/cpu golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.29.0 -## explicit; go 1.18 +# golang.org/x/term v0.32.0 +## explicit; go 1.23.0 golang.org/x/term diff --git a/trunk/Dockerfile.builds b/trunk/Dockerfile.builds index 4a9f89a0e..692db26ef 100644 --- a/trunk/Dockerfile.builds +++ b/trunk/Dockerfile.builds @@ -26,7 +26,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off && make FROM ossrs/srs:ubuntu16-cache AS ubuntu16-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make ######################################################## FROM ossrs/srs:ubuntu18-cache AS ubuntu18-baseline @@ -35,7 +35,7 @@ RUN cd /srs/trunk && ./configure --srt=off --gb28181=off && make FROM ossrs/srs:ubuntu18-cache AS ubuntu18-all COPY . /srs -RUN cd /srs/trunk && ./configure --srt=on --gb28181=on --h265=on && make +RUN cd /srs/trunk && ./configure --srt=on --gb28181=on && make ######################################################## FROM ossrs/srs:ubuntu20-cache AS ubuntu20-baseline diff --git a/trunk/Dockerfile.test b/trunk/Dockerfile.test index aae3f0918..cd7bde7c5 100644 --- a/trunk/Dockerfile.test +++ b/trunk/Dockerfile.test @@ -18,7 +18,7 @@ WORKDIR /srs/trunk # Note that we must enable the gcc7 or link failed. # Please note that we must disable the ffmpeg-opus, as it negatively impacts performance. We may consider # enabling it in the future when support for multi-threading transcoding is available. -RUN ./configure --srt=on --gb28181=on --srt=on --apm=on --h265=on --utest=on --ffmpeg-opus=off --build-cache=on +RUN ./configure --srt=on --gb28181=on --srt=on --apm=on --utest=on --ffmpeg-opus=off --build-cache=on RUN make utest ${MAKEARGS} # Build benchmark tool. diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh index 5a85fd2a2..f15bbb8b2 100755 --- a/trunk/auto/auto_headers.sh +++ b/trunk/auto/auto_headers.sh @@ -92,11 +92,8 @@ else srs_undefine_macro "SRS_FFMPEG_OPUS" $SRS_AUTO_HEADERS_H fi -if [[ $SRS_H265 == YES ]]; then - srs_define_macro "SRS_H265" $SRS_AUTO_HEADERS_H -else - srs_undefine_macro "SRS_H265" $SRS_AUTO_HEADERS_H -fi +# H.265/HEVC support is always enabled +srs_define_macro "SRS_H265" $SRS_AUTO_HEADERS_H if [[ $SRS_SIMULATOR == YES ]]; then srs_define_macro "SRS_SIMULATOR" $SRS_AUTO_HEADERS_H diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 9a186f462..1b36046cd 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -6,7 +6,8 @@ help=no SRS_HDS=NO SRS_SRT=YES SRS_RTC=YES -SRS_H265=YES +# SRS_H265 is always enabled, no longer configurable +SRS_H265=RESERVED SRS_GB28181=NO SRS_CXX11=YES SRS_CXX14=NO @@ -189,7 +190,7 @@ Features: --ffmpeg-fit=on|off Whether enable the FFmpeg fit(source code). Default: $(value2switch $SRS_FFMPEG_FIT) --ffmpeg-opus=on|off Whether enable the FFmpeg native opus codec. Default: $(value2switch $SRS_FFMPEG_OPUS) --apm=on|off Whether enable cloud logging and APM(Application Performance Monitor). Default: $(value2switch $SRS_APM) - --h265=on|off Whether build the HEVC(H.265) support. Default: $(value2switch $SRS_H265) + --h265=on Whether build the HEVC(H.265) support. Always enabled. --prefix= The absolute installation path. Default: $SRS_PREFIX --jobs[=N] Allow N jobs at once; infinite jobs with no arg. Default: $SRS_JOBS @@ -601,6 +602,12 @@ function apply_auto_options() { if [[ ! -z SRS_JOBS ]]; then export SRS_JOBS="--jobs=${SRS_JOBS}" fi + + # H.265/HEVC is always enabled, see https://github.com/ossrs/srs/issues/4349 + if [[ $SRS_H265 != RESERVED ]]; then + echo "Warning: --h265 option is deprecated. H.265/HEVC support is always enabled." + SRS_H265=ON + fi } apply_auto_options @@ -654,7 +661,7 @@ function regenerate_options() { SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --srt=$(value2switch $SRS_SRT)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sys-srt=$(value2switch $SRS_USE_SYS_SRT)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --rtc=$(value2switch $SRS_RTC)" - SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --h265=$(value2switch $SRS_H265)" + SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --gb28181=$(value2switch $SRS_GB28181)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --simulator=$(value2switch $SRS_SIMULATOR)" SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --cxx11=$(value2switch $SRS_CXX11)" diff --git a/trunk/configure b/trunk/configure index 0a2e5cc26..7e59afbcd 100755 --- a/trunk/configure +++ b/trunk/configure @@ -736,11 +736,7 @@ if [[ $SRS_GB28181 == YES ]]; then else echo -e "${GREEN}Warning: GB28181 is disabled.${BLACK}" fi -if [[ $SRS_H265 == YES ]]; then - echo -e "${YELLOW}Experiment: HEVC/H.265 is enabled. https://github.com/ossrs/srs/issues/465${BLACK}" -else - echo -e "${GREEN}Warning: HEVC/H.265 is disabled.${BLACK}" -fi +echo -e "${GREEN}Experiment: HEVC/H.265 is enabled. https://github.com/ossrs/srs/issues/465${BLACK}" if [[ $SRS_SRT == YES ]]; then echo -e "${YELLOW}Experiment: SRT is enabled. https://github.com/ossrs/srs/issues/1147${BLACK}" else diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 7f8364966..01144bb74 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-07-04, Merge [#4412](https://github.com/ossrs/srs/pull/4412): Refine code and add tests for #4289. v7.0.45 (#4412) * v7.0, 2025-07-04, Merge [#4413](https://github.com/ossrs/srs/pull/4413): RTMP2RTC: Support dual video track for bridge. v7.0.44 (#4413) * v7.0, 2025-07-03, Merge [#4349](https://github.com/ossrs/srs/pull/4349): rtc2rtmp: Support WebRTC-to-RTMP conversion with HEVC. v7.0.43 (#4349) * v7.0, 2025-06-04, Merge [#4310](https://github.com/ossrs/srs/pull/4310): Player: Get codec by webrtc api: pc.getStats. v7.0.42 (#4310) diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 640445fcc..4b46d303f 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -870,9 +870,7 @@ srs_error_t SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) int size = msg->size; bool codec_ok = SrsFlvVideo::h264(payload, size); -#ifdef SRS_H265 codec_ok = codec_ok? true : SrsFlvVideo::hevc(payload, size); -#endif bool is_key_frame = codec_ok && SrsFlvVideo::keyframe(payload, size) && !SrsFlvVideo::sh(payload, size); if (!is_key_frame) { diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index e9ae2390e..07d6a190a 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -1571,11 +1571,9 @@ SrsGbMuxer::SrsGbMuxer(SrsGbSession* session) h264_pps_changed_ = false; h264_sps_pps_sent_ = false; -#ifdef SRS_H265 hevc_ = new SrsRawHEVCStream(); vps_sps_pps_sent_ = false; vps_sps_pps_change_ = false; -#endif aac_ = new SrsRawAacStream(); @@ -1588,9 +1586,7 @@ SrsGbMuxer::~SrsGbMuxer() close(); srs_freep(avc_); -#ifdef SRS_H265 srs_freep(hevc_); -#endif srs_freep(aac_); srs_freep(queue_); srs_freep(pprint_); @@ -1635,12 +1631,10 @@ srs_error_t SrsGbMuxer::on_ts_video(SrsTsMessage* msg, SrsBuffer* avs) if ((err = mux_h264(msg, avs)) != srs_success){ return srs_error_wrap(err, "mux h264"); } -#ifdef SRS_H265 } else if (h->ctx_->video_stream_type_ == SrsTsStreamVideoHEVC) { if ((err = mux_h265(msg, avs)) != srs_success){ return srs_error_wrap(err, "mux hevc"); } -#endif } else { return srs_error_new(ERROR_STREAM_CASTER_TS_CODEC, "ts: unsupported stream codec=%d", h->ctx_->video_stream_type_); } @@ -1810,7 +1804,6 @@ srs_error_t SrsGbMuxer::write_h264_ipb_frame(char* frame, int frame_size, uint32 return rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv); } -#ifdef SRS_H265 srs_error_t SrsGbMuxer::mux_h265(SrsTsMessage *msg, SrsBuffer *avs) { srs_error_t err = srs_success; @@ -1986,7 +1979,6 @@ srs_error_t SrsGbMuxer::write_h265_ipb_frame(char* frame, int frame_size, uint32 return err; } -#endif srs_error_t SrsGbMuxer::on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs) { @@ -2603,12 +2595,6 @@ srs_error_t SrsRecoverablePsContext::decode(SrsBuffer* stream, ISrsPsMessageHand if ((err = ctx_.decode(stream, handler)) != srs_success) { return enter_recover_mode(stream, handler, stream->pos(), srs_error_wrap(err, "decode pack")); } -#ifndef SRS_H265 - // Check stream type, error if HEVC, because not supported yet. - if (ctx_.video_stream_type_ == SrsTsStreamVideoHEVC) { - return srs_error_new(ERROR_GB_PS_HEADER, "HEVC is not supported"); - } -#endif return err; } diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 9049bbd5e..bd9c4dc9f 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -36,9 +36,7 @@ class SrsGbMuxer; class SrsSimpleRtmpClient; struct SrsRawAacStreamCodec; class SrsRawH264Stream; -#ifdef SRS_H265 class SrsRawHEVCStream; -#endif class SrsSharedPtrMessage; class SrsPithyPrint; class SrsRawAacStream; @@ -469,14 +467,12 @@ private: bool h264_pps_changed_; bool h264_sps_pps_sent_; -#ifdef SRS_H265 SrsRawHEVCStream* hevc_; bool vps_sps_pps_change_; std::string h265_vps_; std::string h265_sps_; std::string h265_pps_; bool vps_sps_pps_sent_; -#endif private: SrsRawAacStream* aac_; std::string aac_specific_config_; @@ -494,11 +490,9 @@ private: virtual srs_error_t mux_h264(SrsTsMessage* msg, SrsBuffer* avs); virtual srs_error_t write_h264_sps_pps(uint32_t dts, uint32_t pts); virtual srs_error_t write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts); -#ifdef SRS_H265 virtual srs_error_t mux_h265(SrsTsMessage* msg, SrsBuffer* avs); virtual srs_error_t write_h265_vps_sps_pps(uint32_t dts, uint32_t pts); virtual srs_error_t write_h265_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts); -#endif virtual srs_error_t on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs); virtual srs_error_t write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts); virtual srs_error_t rtmp_write_packet(char type, uint32_t timestamp, char* data, int size); diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index cfa461049..ab090ff6d 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -3171,7 +3171,7 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRtcUserConfig* ruc, s const SrsMediaPayloadType& payload = payloads.at(j); // For H.265, we only check if profile-id=1 (Main Profile) - // Format example: level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST + // Format example: level-id=156;profile-id=1;tier-flag=0;tx-mode=SRST if (!has_main_profile || srs_sdp_has_h265_profile(payload, "1")) { remote_payload = payload; break; diff --git a/trunk/src/app/srs_app_rtc_source.cpp b/trunk/src/app/srs_app_rtc_source.cpp index 0d5a8d944..82cc40fa8 100644 --- a/trunk/src/app/srs_app_rtc_source.cpp +++ b/trunk/src/app/srs_app_rtc_source.cpp @@ -814,16 +814,17 @@ std::vector SrsRtcSource::get_track_desc(std::string ty if (type == "video") { std::vector::iterator it = stream_desc_->video_track_descs_.begin(); - while (it != stream_desc_->video_track_descs_.end() ){ + for (; it != stream_desc_->video_track_descs_.end(); ++it){ + SrsRtcTrackDescription* track_desc = *it; + if (media_name.empty()) { - track_descs.push_back(*it); + track_descs.push_back(track_desc); } else { - SrsVideoCodecId codec = SrsVideoCodecId((*it)->media_->codec(true)); + SrsVideoCodecId codec = SrsVideoCodecId(track_desc->media_->codec(true)); if (codec == srs_video_codec_str2id(media_name)) { - track_descs.push_back(*it); + track_descs.push_back(track_desc); } } - ++it; } } @@ -862,7 +863,7 @@ SrsRtcRtpBuilder::SrsRtcRtpBuilder(SrsFrameToRtcBridge* bridge, SrsSharedPtrvcodec->id == SrsVideoCodecIdHEVC) { for (size_t i = 0; i < format->vcodec->hevc_dec_conf_record_.nalu_vec.size(); i++) { - if (format->vcodec->hevc_dec_conf_record_.nalu_vec[i].nal_unit_type == SrsHevcNaluType_VPS - || format->vcodec->hevc_dec_conf_record_.nalu_vec[i].nal_unit_type == SrsHevcNaluType_SPS - || format->vcodec->hevc_dec_conf_record_.nalu_vec[i].nal_unit_type == SrsHevcNaluType_PPS) { - vector& nalu = (vector&)format->vcodec->hevc_dec_conf_record_.nalu_vec[i].nal_data_vec[0].nal_unit_data; - params.push_back(&nalu); - size += format->vcodec->hevc_dec_conf_record_.nalu_vec[i].nal_data_vec[0].nal_unit_length; + const SrsHevcHvccNalu& nalu = format->vcodec->hevc_dec_conf_record_.nalu_vec[i]; + if (nalu.nal_unit_type == SrsHevcNaluType_VPS + || nalu.nal_unit_type == SrsHevcNaluType_SPS + || nalu.nal_unit_type == SrsHevcNaluType_PPS) { + const SrsHevcNalData& nal_data = nalu.nal_data_vec[0]; + params.push_back(&(vector&)nal_data.nal_unit_data); + size += nal_data.nal_unit_length; } } @@ -2703,7 +2705,7 @@ srs_error_t SrsVideoPayload::set_h264_param_desc(std::string fmtp) return err; } -// level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST +// level-id=156;profile-id=1;tier-flag=0;tx-mode=SRST srs_error_t SrsVideoPayload::set_h265_param_desc(std::string fmtp) { std::vector attributes = split_str(fmtp, ";"); diff --git a/trunk/src/app/srs_app_source.cpp b/trunk/src/app/srs_app_source.cpp index c17f85f2b..b3a4d89d4 100755 --- a/trunk/src/app/srs_app_source.cpp +++ b/trunk/src/app/srs_app_source.cpp @@ -623,9 +623,7 @@ srs_error_t SrsGopCache::cache(SrsSharedPtrMessage* shared_msg) if (msg->is_video()) { // Drop video when not h.264 or h.265. bool codec_ok = SrsFlvVideo::h264(msg->payload, msg->size); -#ifdef SRS_H265 codec_ok = codec_ok ? true : SrsFlvVideo::hevc(msg->payload, msg->size); -#endif if (!codec_ok) return err; cached_video_count++; @@ -1046,13 +1044,11 @@ srs_error_t SrsOriginHub::on_video(SrsSharedPtrMessage* shared_video, bool is_se srs_trace("%dB video sh, codec(%d, profile=%s, level=%s, %dx%d, %dkbps, %.1ffps, %.1fs)", msg->size, c->id, srs_avc_profile2str(c->avc_profile).c_str(), srs_avc_level2str(c->avc_level).c_str(), c->width, c->height, c->video_data_rate / 1000, c->frame_rate, c->duration); -#ifdef SRS_H265 } else if (c->id == SrsVideoCodecIdHEVC) { err = stat->on_video_info(req_, c->id, c->hevc_profile, c->hevc_level, c->width, c->height); srs_trace("%dB video sh, codec(%d, profile=%s, level=%s, %dx%d, %dkbps, %.1ffps, %.1fs)", msg->size, c->id, srs_hevc_profile2str(c->hevc_profile).c_str(), srs_hevc_level2str(c->hevc_level).c_str(), c->width, c->height, c->video_data_rate / 1000, c->frame_rate, c->duration); -#endif } if (err != srs_success) { return srs_error_wrap(err, "stat video"); diff --git a/trunk/src/app/srs_app_srt_source.cpp b/trunk/src/app/srs_app_srt_source.cpp index 860cd01e1..01dd2dc9b 100644 --- a/trunk/src/app/srs_app_srt_source.cpp +++ b/trunk/src/app/srs_app_srt_source.cpp @@ -372,13 +372,11 @@ srs_error_t SrsSrtFrameBuilder::on_ts_message(SrsTsMessage* msg) } // TODO: FIXME: implements other codec? -#ifdef SRS_H265 if (msg->channel->stream == SrsTsStreamVideoHEVC) { if ((err = on_ts_video_hevc(msg, &avs)) != srs_success) { return srs_error_wrap(err, "ts: consume hevc video"); } } -#endif return err; } @@ -555,7 +553,6 @@ srs_error_t SrsSrtFrameBuilder::on_h264_frame(SrsTsMessage* msg, vector >& ipb_frames); srs_error_t check_audio_sh_change(SrsTsMessage* msg, uint32_t pts); srs_error_t on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size); -#ifdef SRS_H265 srs_error_t on_ts_video_hevc(SrsTsMessage *msg, SrsBuffer *avs); srs_error_t check_vps_sps_pps_change(SrsTsMessage *msg); srs_error_t on_hevc_frame(SrsTsMessage *msg, std::vector< std::pair > &ipb_frames); -#endif private: ISrsStreamBridge* bridge_; private: @@ -135,12 +133,10 @@ private: bool sps_pps_change_; std::string sps_; std::string pps_; -#ifdef SRS_H265 bool vps_sps_pps_change_; std::string hevc_vps_; std::string hevc_sps_; std::vector hevc_pps_; -#endif // Record audio sepcific config had changed, if change, need to generate new audio sh frame. bool audio_sh_change_; std::string audio_sh_; diff --git a/trunk/src/app/srs_app_statistic.cpp b/trunk/src/app/srs_app_statistic.cpp index 57e09063b..d30f10ffd 100644 --- a/trunk/src/app/srs_app_statistic.cpp +++ b/trunk/src/app/srs_app_statistic.cpp @@ -147,11 +147,9 @@ srs_error_t SrsStatisticStream::dumps(SrsJsonObject* obj) if (vcodec == SrsVideoCodecIdAVC) { video->set("profile", SrsJsonAny::str(srs_avc_profile2str(avc_profile).c_str())); video->set("level", SrsJsonAny::str(srs_avc_level2str(avc_level).c_str())); -#ifdef SRS_H265 } else if (vcodec == SrsVideoCodecIdHEVC) { video->set("profile", SrsJsonAny::str(srs_hevc_profile2str(hevc_profile).c_str())); video->set("level", SrsJsonAny::str(srs_hevc_level2str(hevc_level).c_str())); -#endif } else { video->set("profile", SrsJsonAny::str("Other")); video->set("level", SrsJsonAny::str("Other")); @@ -360,11 +358,9 @@ srs_error_t SrsStatistic::on_video_info(SrsRequest* req, SrsVideoCodecId vcodec, if (vcodec == SrsVideoCodecIdAVC) { stream->avc_profile = (SrsAvcProfile)profile; stream->avc_level = (SrsAvcLevel)level; -#ifdef SRS_H265 } else if (vcodec == SrsVideoCodecIdHEVC) { stream->hevc_profile = (SrsHevcProfile)profile; stream->hevc_level = (SrsHevcLevel)level; -#endif } else { stream->avc_profile = (SrsAvcProfile)profile; stream->avc_level = (SrsAvcLevel)level; @@ -664,7 +660,6 @@ void SrsStatistic::dumps_hints_kv(std::stringstream & ss) ss << "&send=" << kbps->get_send_kbps_30s(); } -#ifdef SRS_H265 // For HEVC, we should check active stream which is HEVC codec. for (std::map::iterator it = streams.begin(); it != streams.end(); it++) { SrsStatisticStream* stream = it->second; @@ -673,7 +668,6 @@ void SrsStatistic::dumps_hints_kv(std::stringstream & ss) break; } } -#endif } #ifdef SRS_APM diff --git a/trunk/src/app/srs_app_statistic.hpp b/trunk/src/app/srs_app_statistic.hpp index e15a0c702..656a0bc27 100644 --- a/trunk/src/app/srs_app_statistic.hpp +++ b/trunk/src/app/srs_app_statistic.hpp @@ -70,12 +70,10 @@ public: SrsAvcProfile avc_profile; // The level_idc, ISO_IEC_14496-10-AVC-2003.pdf, page 45. SrsAvcLevel avc_level; -#ifdef SRS_H265 // The profile_idc, ITU-T-H.265-2021.pdf, page 62. SrsHevcProfile hevc_profile; // The level_idc, ITU-T-H.265-2021.pdf, page 63. SrsHevcLevel hevc_level; -#endif // The width and height in codec info. int width; int height; diff --git a/trunk/src/app/srs_app_stream_bridge.cpp b/trunk/src/app/srs_app_stream_bridge.cpp index 1a06ce365..cc9bf0e89 100644 --- a/trunk/src/app/srs_app_stream_bridge.cpp +++ b/trunk/src/app/srs_app_stream_bridge.cpp @@ -68,6 +68,7 @@ SrsFrameToRtcBridge::SrsFrameToRtcBridge(SrsSharedPtr source) source_ = source; #if defined(SRS_FFMPEG_FIT) + // Use lazy initialization - no need to determine codec/track parameters here rtp_builder_ = new SrsRtcRtpBuilder(this, source); #endif } @@ -131,8 +132,6 @@ srs_error_t SrsFrameToRtcBridge::on_rtp(SrsRtpPacket* pkt) return source_->on_rtp(pkt); } - - #endif SrsCompositeBridge::SrsCompositeBridge() diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index a836beea1..5a48c9b2f 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 44 +#define VERSION_REVISION 45 #endif \ No newline at end of file diff --git a/trunk/src/kernel/srs_kernel_codec.cpp b/trunk/src/kernel/srs_kernel_codec.cpp index bcb516438..22a178c4e 100644 --- a/trunk/src/kernel/srs_kernel_codec.cpp +++ b/trunk/src/kernel/srs_kernel_codec.cpp @@ -206,9 +206,7 @@ bool SrsFlvVideo::sh(char* data, int size) { // Check sequence header only for H.264 or H.265 bool codec_ok = h264(data, size); -#ifdef SRS_H265 codec_ok = codec_ok? true : hevc(data, size); -#endif if (!codec_ok) return false; // 2bytes required. @@ -247,7 +245,6 @@ bool SrsFlvVideo::h264(char* data, int size) return codec_id == SrsVideoCodecIdAVC; } -#ifdef SRS_H265 bool SrsFlvVideo::hevc(char* data, int size) { // 1bytes required. @@ -276,7 +273,6 @@ bool SrsFlvVideo::hevc(char* data, int size) return codec_id == SrsVideoCodecIdHEVC; } -#endif bool SrsFlvVideo::acceptable(char* data, int size) { @@ -505,8 +501,6 @@ string srs_avc_level2str(SrsAvcLevel level) } } -#ifdef SRS_H265 - string srs_hevc_profile2str(SrsHevcProfile profile) { switch (profile) { @@ -538,8 +532,6 @@ string srs_hevc_level2str(SrsHevcLevel level) } } -#endif - SrsSample::SrsSample() { size = 0; @@ -705,13 +697,9 @@ srs_error_t SrsVideoFrame::add_sample(char* bytes, int size) // For HEVC(H.265), try to parse the IDR from NALUs. if (c && c->id == SrsVideoCodecIdHEVC) { -#ifdef SRS_H265 SrsHevcNaluType nalu_type = SrsHevcNaluTypeParse(bytes[0]); has_idr = SrsIsIRAP(nalu_type); return err; -#else - return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled"); -#endif } // By default, use AVC(H.264) to parse NALU. @@ -791,7 +779,6 @@ srs_error_t SrsVideoFrame::parse_avc_bframe(const SrsSample* sample, bool& is_b_ return err; } -#ifdef SRS_H265 srs_error_t SrsVideoFrame::parse_hevc_nalu_type(const SrsSample* sample, SrsHevcNaluType& hevc_nalu_type) { srs_error_t err = srs_success; @@ -872,7 +859,6 @@ srs_error_t SrsVideoFrame::parse_hevc_bframe(const SrsSample* sample, SrsFormat return err; } -#endif SrsFormat::SrsFormat() { @@ -1094,9 +1080,7 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) // Check codec for H.264 and H.265. bool codec_ok = (codec_id == SrsVideoCodecIdAVC); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (codec_id == SrsVideoCodecIdHEVC); -#endif if (!codec_ok) { return srs_error_new(ERROR_HLS_DECODE_ERROR, "only support video H.264/H.265, actual=%d", codec_id); } @@ -1131,7 +1115,6 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) // Parse sequence header for H.265/HEVC. if (codec_id == SrsVideoCodecIdHEVC) { -#ifdef SRS_H265 if (packet_type == SrsVideoAvcFrameTraitSequenceHeader) { // TODO: demux vps/sps/pps for hevc if ((err = hevc_demux_hvcc(stream)) != srs_success) { @@ -1144,9 +1127,6 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) } } return err; -#else - return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled"); -#endif } // Parse sequence header for H.264/AVC. @@ -1169,7 +1149,6 @@ srs_error_t SrsFormat::video_avc_demux(SrsBuffer* stream, int64_t timestamp) // For media server, we don't care the codec, so we just try to parse sps-pps, and we could ignore any error if fail. // LCOV_EXCL_START -#ifdef SRS_H265 // struct ptl SrsHevcProfileTierLevel::SrsHevcProfileTierLevel() { @@ -2257,8 +2236,6 @@ srs_error_t SrsFormat::hevc_demux_rbsp_ptl(SrsBitBuffer* bs, SrsHevcProfileTierL return err; } -#endif - srs_error_t SrsFormat::avc_demux_sps_pps(SrsBuffer* stream) { // AVCDecoderConfigurationRecord @@ -2615,12 +2592,8 @@ srs_error_t SrsFormat::video_nalu_demux(SrsBuffer* stream) } if (vcodec->id == SrsVideoCodecIdHEVC) { -#ifdef SRS_H265 // TODO: FIXME: Might need to guess format? return do_avc_demux_ibmf_format(stream); -#else - return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled"); -#endif } // Parse the SPS/PPS in ANNEXB or IBMF format. diff --git a/trunk/src/kernel/srs_kernel_codec.hpp b/trunk/src/kernel/srs_kernel_codec.hpp index b2f2b00b8..638bacd60 100644 --- a/trunk/src/kernel/srs_kernel_codec.hpp +++ b/trunk/src/kernel/srs_kernel_codec.hpp @@ -297,10 +297,8 @@ public: * check codec h264. */ static bool h264(char* data, int size); -#ifdef SRS_H265 // Check whether codec is HEVC(H.265). static bool hevc(char* data, int size); -#endif /** * check the video RTMP/flv header info, * @return true if video RTMP/flv header is ok. @@ -439,7 +437,6 @@ enum SrsAvcNaluType #define SrsAvcNaluTypeParse(code) (SrsAvcNaluType)(code & 0x1F) std::string srs_avc_nalu2str(SrsAvcNaluType nalu_type); -#ifdef SRS_H265 /** * The enum NALU type for HEVC * @see Table 7-1 – NAL unit type codes and NAL unit type classes @@ -975,8 +972,6 @@ struct SrsHevcDecoderConfigurationRecord SrsHevcRbspPps pps_table[SrsHevcMax_PPS_COUNT]; }; -#endif - /** * Table 7-6 – Name association to slice_type * ISO_IEC_14496-10-AVC-2012.pdf, page 105. @@ -1100,8 +1095,6 @@ enum SrsAvcLevel }; std::string srs_avc_level2str(SrsAvcLevel level); -#ifdef SRS_H265 - /** * the profile for hevc/h.265, Annex A Profiles, tiers and levels * @see A.3 Profiles @@ -1144,8 +1137,6 @@ enum SrsHevcLevel }; std::string srs_hevc_level2str(SrsHevcLevel level); -#endif - /** * A sample is the unit of frame. * It's a NALU for H.264, H.265. @@ -1256,12 +1247,10 @@ public: SrsAvcProfile avc_profile; // level_idc, ISO_IEC_14496-10-AVC-2003.pdf, page 45. SrsAvcLevel avc_level; -#ifdef SRS_H265 // The profile_idc, ITU-T-H.265-2021.pdf, page 62. SrsHevcProfile hevc_profile; // The level_idc, ITU-T-H.265-2021.pdf, page 63. SrsHevcLevel hevc_level; -#endif // lengthSizeMinusOne, ISO_IEC_14496-15-AVC-format-2012.pdf, page 16 int8_t NAL_unit_length; // Note that we may resize the vector, so the under-layer bytes may change. @@ -1270,10 +1259,8 @@ public: public: // the avc payload format. SrsAvcPayloadFormat payload_format; -#ifdef SRS_H265 public: SrsHevcDecoderConfigurationRecord hevc_dec_conf_record_; -#endif public: SrsVideoCodecConfig(); virtual ~SrsVideoCodecConfig(); @@ -1349,10 +1336,8 @@ public: public: static srs_error_t parse_avc_nalu_type(const SrsSample* sample, SrsAvcNaluType& avc_nalu_type); static srs_error_t parse_avc_bframe(const SrsSample* sample, bool& is_b_frame); -#ifdef SRS_H265 static srs_error_t parse_hevc_nalu_type(const SrsSample* sample, SrsHevcNaluType& hevc_nalu_type); static srs_error_t parse_hevc_bframe(const SrsSample* sample, SrsFormat* format, bool& is_b_frame); -#endif }; /** @@ -1400,7 +1385,6 @@ private: // Demux the sps/pps from sequence header. // Demux the samples from NALUs. virtual srs_error_t video_avc_demux(SrsBuffer* stream, int64_t timestamp); -#ifdef SRS_H265 private: virtual srs_error_t hevc_demux_hvcc(SrsBuffer* stream); private: @@ -1413,7 +1397,6 @@ public: virtual srs_error_t hevc_demux_vps(SrsBuffer *stream); virtual srs_error_t hevc_demux_sps(SrsBuffer *stream); virtual srs_error_t hevc_demux_pps(SrsBuffer *stream); -#endif private: // Parse the H.264 SPS/PPS. virtual srs_error_t avc_demux_sps_pps(SrsBuffer* stream); diff --git a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp index d2ab5a49a..885191df2 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtp.cpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtp.cpp @@ -1568,7 +1568,6 @@ ISrsRtpPayloader* SrsRtpFUAPayload2::copy() return cp; } -#ifdef SRS_H265 SrsRtpSTAPPayloadHevc::SrsRtpSTAPPayloadHevc() { ++_srs_pps_objs_rothers->sugar; @@ -1927,4 +1926,4 @@ ISrsRtpPayloader* SrsRtpFUAPayloadHevc2::copy() return cp; } -#endif \ No newline at end of file + diff --git a/trunk/src/kernel/srs_kernel_rtc_rtp.hpp b/trunk/src/kernel/srs_kernel_rtc_rtp.hpp index f69732b3c..09d48008b 100644 --- a/trunk/src/kernel/srs_kernel_rtc_rtp.hpp +++ b/trunk/src/kernel/srs_kernel_rtc_rtp.hpp @@ -478,7 +478,6 @@ public: virtual ISrsRtpPayloader* copy(); }; -#ifdef SRS_H265 class SrsRtpSTAPPayloadHevc : public ISrsRtpPayloader { public: @@ -542,6 +541,5 @@ public: virtual srs_error_t decode(SrsBuffer* buf); virtual ISrsRtpPayloader* copy(); }; -#endif #endif diff --git a/trunk/src/kernel/srs_kernel_ts.cpp b/trunk/src/kernel/srs_kernel_ts.cpp index 9492852fb..73293a07a 100644 --- a/trunk/src/kernel/srs_kernel_ts.cpp +++ b/trunk/src/kernel/srs_kernel_ts.cpp @@ -43,9 +43,7 @@ string srs_ts_stream2string(SrsTsStream stream) case SrsTsStreamAudioAC3: return "AC3"; case SrsTsStreamAudioDTS: return "AudioDTS"; case SrsTsStreamVideoH264: return "H.264"; -#ifdef SRS_H265 case SrsTsStreamVideoHEVC: return "H.265"; -#endif case SrsTsStreamVideoMpeg4: return "MP4"; case SrsTsStreamAudioMpeg4: return "MP4A"; default: return "Other"; @@ -290,13 +288,9 @@ srs_error_t SrsTsContext::encode(ISrsStreamWriter* writer, SrsTsMessage* msg, Sr video_pid = TS_VIDEO_AVC_PID; break; case SrsVideoCodecIdHEVC: -#ifdef SRS_H265 vs = SrsTsStreamVideoHEVC; video_pid = TS_VIDEO_AVC_PID; break; -#else - return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled"); -#endif case SrsVideoCodecIdDisabled: vs = SrsTsStreamReserved; break; @@ -371,9 +365,7 @@ srs_error_t SrsTsContext::encode_pat_pmt(ISrsStreamWriter* writer, int16_t vpid, srs_error_t err = srs_success; bool codec_ok = (vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (vs == SrsTsStreamVideoHEVC); -#endif if (!codec_ok) { return srs_error_new(ERROR_HLS_NO_STREAM, "ts: no PID, vs=%d, as=%d", vs, as); } @@ -441,9 +433,7 @@ srs_error_t SrsTsContext::encode_pes(ISrsStreamWriter* writer, SrsTsMessage* msg } bool codec_ok = (sid == SrsTsStreamVideoH264 || sid == SrsTsStreamAudioAAC || sid == SrsTsStreamAudioMp3); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (sid == SrsTsStreamVideoHEVC); -#endif if (!codec_ok) { srs_info("ts: ignore the unknown stream, sid=%d", sid); return err; @@ -770,9 +760,7 @@ SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context, // Here we must get the correct codec. bool codec_ok = (vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (vs == SrsTsStreamVideoHEVC); -#endif srs_assert(codec_ok); // if mp3 or aac specified, use audio to carry pcr. @@ -785,9 +773,7 @@ SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context, // If h.264/h.265 specified, use video to carry pcr. codec_ok = (vs == SrsTsStreamVideoH264); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (vs == SrsTsStreamVideoHEVC); -#endif if (codec_ok) { pmt->PCR_PID = vpid; pmt->infos.push_back(new SrsTsPayloadPMTESInfo(vs, vpid)); @@ -2563,9 +2549,7 @@ srs_error_t SrsTsPayloadPMT::psi_decode(SrsBuffer* stream) // update the apply pid table switch (info->stream_type) { case SrsTsStreamVideoH264: -#ifdef SRS_H265 case SrsTsStreamVideoHEVC: -#endif case SrsTsStreamVideoMpeg4: packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type); break; @@ -2649,9 +2633,7 @@ srs_error_t SrsTsPayloadPMT::psi_encode(SrsBuffer* stream) // update the apply pid table switch (info->stream_type) { case SrsTsStreamVideoH264: -#ifdef SRS_H265 case SrsTsStreamVideoHEVC: -#endif case SrsTsStreamVideoMpeg4: packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type); break; @@ -2884,11 +2866,7 @@ srs_error_t SrsTsMessageCache::cache_video(SrsVideoFrame* frame, int64_t dts) // Write H.265 video frame to cache. if (frame && frame->vcodec()->id == SrsVideoCodecIdHEVC) { -#ifdef SRS_H265 return do_cache_hevc(frame); -#else - return srs_error_new(ERROR_HEVC_DISABLED, "H.265 is disabled"); -#endif } // Write H.264 video frame to cache. @@ -3122,7 +3100,6 @@ srs_error_t SrsTsMessageCache::do_cache_avc(SrsVideoFrame* frame) return err; } -#ifdef SRS_H265 srs_error_t SrsTsMessageCache::do_cache_hevc(SrsVideoFrame* frame) { srs_error_t err = srs_success; @@ -3165,7 +3142,6 @@ srs_error_t SrsTsMessageCache::do_cache_hevc(SrsVideoFrame* frame) return err; } -#endif SrsTsTransmuxer::SrsTsTransmuxer() { @@ -3306,9 +3282,7 @@ srs_error_t SrsTsTransmuxer::write_video(int64_t timestamp, char* data, int size } bool codec_ok = (format->vcodec->id == SrsVideoCodecIdAVC); -#ifdef SRS_H265 codec_ok = codec_ok ? true : (format->vcodec->id == SrsVideoCodecIdHEVC); -#endif if (!codec_ok) { return err; } diff --git a/trunk/src/kernel/srs_kernel_ts.hpp b/trunk/src/kernel/srs_kernel_ts.hpp index b072299c2..b25558e7c 100644 --- a/trunk/src/kernel/srs_kernel_ts.hpp +++ b/trunk/src/kernel/srs_kernel_ts.hpp @@ -1321,9 +1321,7 @@ private: virtual srs_error_t do_cache_mp3(SrsAudioFrame* frame); virtual srs_error_t do_cache_aac(SrsAudioFrame* frame); virtual srs_error_t do_cache_avc(SrsVideoFrame* frame); -#ifdef SRS_H265 virtual srs_error_t do_cache_hevc(SrsVideoFrame* frame); -#endif }; // Transmux the RTMP stream to HTTP-TS stream. diff --git a/trunk/src/protocol/srs_protocol_raw_avc.cpp b/trunk/src/protocol/srs_protocol_raw_avc.cpp index bc22e2520..f89753d36 100644 --- a/trunk/src/protocol/srs_protocol_raw_avc.cpp +++ b/trunk/src/protocol/srs_protocol_raw_avc.cpp @@ -261,8 +261,6 @@ srs_error_t SrsRawH264Stream::mux_avc2flv(string video, int8_t frame_type, int8_ return err; } -#ifdef SRS_H265 - SrsRawHEVCStream::SrsRawHEVCStream() { } @@ -616,8 +614,6 @@ srs_error_t SrsRawHEVCStream::mux_avc2flv_enhanced(std::string video, int8_t fra return err; } -#endif - SrsRawAacStream::SrsRawAacStream() { } diff --git a/trunk/src/protocol/srs_protocol_raw_avc.hpp b/trunk/src/protocol/srs_protocol_raw_avc.hpp index e9402cc56..bec8bda1c 100644 --- a/trunk/src/protocol/srs_protocol_raw_avc.hpp +++ b/trunk/src/protocol/srs_protocol_raw_avc.hpp @@ -53,7 +53,6 @@ public: virtual srs_error_t mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, uint32_t dts, uint32_t pts, char** flv, int* nb_flv); }; -#ifdef SRS_H265 // The raw h.265 stream, in annexb. class SrsRawHEVCStream { @@ -105,7 +104,6 @@ public: // This affects other modules like SRT and GB28181, so should be done in a separate refactoring. virtual srs_error_t mux_avc2flv_enhanced(std::string video, int8_t frame_type, int8_t packet_type, uint32_t dts, uint32_t pts, char **flv, int *nb_flv); }; -#endif // The header of adts sample. struct SrsRawAacStreamCodec diff --git a/trunk/src/utest/srs_utest_avc.cpp b/trunk/src/utest/srs_utest_avc.cpp index c05dea38c..464f63874 100644 --- a/trunk/src/utest/srs_utest_avc.cpp +++ b/trunk/src/utest/srs_utest_avc.cpp @@ -600,8 +600,6 @@ VOID TEST(SrsAVCTest, AACMuxToFLV) } } -#ifdef SRS_H265 - VOID TEST(SrsAVCTest, HevcMultiPPS) { srs_error_t err; @@ -686,5 +684,3 @@ VOID TEST(SrsAVCTest, HevcMultiPPS) EXPECT_TRUE(stream.empty()); } -#endif - diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 7b0b51a65..9caebc1df 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -3656,7 +3656,6 @@ VOID TEST(KernelCodecTest, VideoFrameH264) } } -#ifdef SRS_H265 VOID TEST(KernelCodecTest, VideoFrameH265) { srs_error_t err; @@ -3764,7 +3763,6 @@ VOID TEST(KernelCodecTest, VideoFrameH265) HELPER_EXPECT_FAILED(SrsVideoFrame::parse_hevc_bframe(&empty_sample, &format, is_b_frame)); } } -#endif VOID TEST(KernelCodecTest, IsSequenceHeaderSpecial) { @@ -4261,7 +4259,6 @@ VOID TEST(KernelCodecTest, VideoFormat) } } -#ifdef SRS_H265 VOID TEST(KernelCodecTest, HevcVideoFormat) { srs_error_t err; @@ -4454,7 +4451,6 @@ VOID TEST(KernelCodecTest, HevcVideoFormat) EXPECT_EQ(1, f.video->nb_samples); } } -#endif VOID TEST(KernelFileTest, FileWriteReader) { @@ -5735,17 +5731,6 @@ VOID TEST(KernelTSTest, CoverContextEncodeHEVC) SrsTsContext ctx; MockTsHandler h; -#ifndef SRS_H265 - if (true) { - MockSrsFileWriter f; - SrsTsMessage m; - - err = ctx.encode(&f, &m, SrsVideoCodecIdHEVC, SrsAudioCodecIdOpus); - HELPER_EXPECT_FAILED(err); - } -#endif - -#ifdef SRS_H265 if (true) { MockSrsFileWriter f; SrsTsMessage m; @@ -5753,7 +5738,6 @@ VOID TEST(KernelTSTest, CoverContextEncodeHEVC) err = ctx.encode(&f, &m, SrsVideoCodecIdHEVC, SrsAudioCodecIdOpus); HELPER_EXPECT_SUCCESS(err); } -#endif } VOID TEST(KernelTSTest, CoverContextDecode) diff --git a/trunk/src/utest/srs_utest_kernel2.cpp b/trunk/src/utest/srs_utest_kernel2.cpp index 72da83bb9..3e059010f 100644 --- a/trunk/src/utest/srs_utest_kernel2.cpp +++ b/trunk/src/utest/srs_utest_kernel2.cpp @@ -426,8 +426,7 @@ VOID TEST(KernelRTMPExtTest, ExtRTMPTest) EXPECT_EQ(SrsVideoAvcFrameTraitNALU, f.video->avc_packet_type); EXPECT_EQ(0x12, f.video->cts); } - -#ifdef SRS_H265 + // For new RTMP enhanced specification, with ext tag header. if (true) { SrsFormat f; @@ -481,7 +480,6 @@ VOID TEST(KernelRTMPExtTest, ExtRTMPTest) HELPER_ASSERT_SUCCESS(f.initialize()); HELPER_EXPECT_FAILED(f.on_video(0, (char*) "\x93mvc1", 5)); } -#endif } VOID TEST(KernelCodecTest, VideoFormatSepcialMProtect_DJI_M30) diff --git a/trunk/src/utest/srs_utest_rtc.cpp b/trunk/src/utest/srs_utest_rtc.cpp index 9603c512c..92014492c 100644 --- a/trunk/src/utest/srs_utest_rtc.cpp +++ b/trunk/src/utest/srs_utest_rtc.cpp @@ -1365,3 +1365,632 @@ VOID TEST(KernelRTCTest, JitterSequence) EXPECT_EQ((uint32_t)11, jitter.correct(11)); } +VOID TEST(KernelRTCTest, H265SDPParsing) +{ + srs_error_t err; + + // Test srs_parse_h265_fmtp with valid parameters + if (true) { + H265SpecificParam h265_param; + string fmtp = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST"; + + HELPER_EXPECT_SUCCESS(srs_parse_h265_fmtp(fmtp, h265_param)); + EXPECT_STREQ("180", h265_param.level_id.c_str()); + EXPECT_STREQ("1", h265_param.profile_id.c_str()); + EXPECT_STREQ("0", h265_param.tier_flag.c_str()); + EXPECT_STREQ("SRST", h265_param.tx_mode.c_str()); + } + + // Test srs_parse_h265_fmtp with different parameter order + if (true) { + H265SpecificParam h265_param; + string fmtp = "profile-id=2;tier-flag=1;level-id=93;tx-mode=MCTS"; + + HELPER_EXPECT_SUCCESS(srs_parse_h265_fmtp(fmtp, h265_param)); + EXPECT_STREQ("93", h265_param.level_id.c_str()); + EXPECT_STREQ("2", h265_param.profile_id.c_str()); + EXPECT_STREQ("1", h265_param.tier_flag.c_str()); + EXPECT_STREQ("MCTS", h265_param.tx_mode.c_str()); + } + + // Test srs_parse_h265_fmtp with missing level-id (should fail) + if (true) { + H265SpecificParam h265_param; + string fmtp = "profile-id=1;tier-flag=0;tx-mode=SRST"; + + HELPER_EXPECT_FAILED(srs_parse_h265_fmtp(fmtp, h265_param)); + } + + // Test srs_parse_h265_fmtp with missing profile-id (should fail) + if (true) { + H265SpecificParam h265_param; + string fmtp = "level-id=180;tier-flag=0;tx-mode=SRST"; + + HELPER_EXPECT_FAILED(srs_parse_h265_fmtp(fmtp, h265_param)); + } + + // Test srs_parse_h265_fmtp with missing tier-flag (should fail) + if (true) { + H265SpecificParam h265_param; + string fmtp = "level-id=180;profile-id=1;tx-mode=SRST"; + + HELPER_EXPECT_FAILED(srs_parse_h265_fmtp(fmtp, h265_param)); + } + + // Test srs_parse_h265_fmtp with missing tx-mode (should fail) + if (true) { + H265SpecificParam h265_param; + string fmtp = "level-id=180;profile-id=1;tier-flag=0"; + + HELPER_EXPECT_FAILED(srs_parse_h265_fmtp(fmtp, h265_param)); + } + + // Test srs_parse_h265_fmtp with empty string (should fail) + if (true) { + H265SpecificParam h265_param; + string fmtp = ""; + + HELPER_EXPECT_FAILED(srs_parse_h265_fmtp(fmtp, h265_param)); + } + + // Test srs_parse_h265_fmtp with extra unknown parameters (should succeed) + if (true) { + H265SpecificParam h265_param; + string fmtp = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST;unknown-param=value"; + + HELPER_EXPECT_SUCCESS(srs_parse_h265_fmtp(fmtp, h265_param)); + EXPECT_STREQ("180", h265_param.level_id.c_str()); + EXPECT_STREQ("1", h265_param.profile_id.c_str()); + EXPECT_STREQ("0", h265_param.tier_flag.c_str()); + EXPECT_STREQ("SRST", h265_param.tx_mode.c_str()); + } +} + +// Forward declarations for H.265 SDP functions (defined in srs_app_rtc_conn.cpp) +extern bool srs_sdp_has_h265_profile(const SrsMediaPayloadType& payload_type, const string& profile); +extern bool srs_sdp_has_h265_profile(const SrsSdp& sdp, const string& profile); + +VOID TEST(KernelRTCTest, H265SDPProfileChecking) +{ + // Test srs_sdp_has_h265_profile with payload type + if (true) { + SrsMediaPayloadType payload_type(96); + payload_type.format_specific_param_ = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST"; + + // Should find Main Profile (profile-id=1) + EXPECT_TRUE(srs_sdp_has_h265_profile(payload_type, "1")); + + // Should not find other profiles + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "2")); + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "3")); + } + + // Test srs_sdp_has_h265_profile with different profile + if (true) { + SrsMediaPayloadType payload_type(97); + payload_type.format_specific_param_ = "level-id=93;profile-id=2;tier-flag=1;tx-mode=MCTS"; + + // Should find Main 10 Profile (profile-id=2) + EXPECT_TRUE(srs_sdp_has_h265_profile(payload_type, "2")); + + // Should not find Main Profile + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "1")); + } + + // Test srs_sdp_has_h265_profile with empty format_specific_param_ + if (true) { + SrsMediaPayloadType payload_type(98); + payload_type.format_specific_param_ = ""; + + // Should not find any profile + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "1")); + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "2")); + } + + // Test srs_sdp_has_h265_profile with invalid format_specific_param_ + if (true) { + SrsMediaPayloadType payload_type(99); + payload_type.format_specific_param_ = "invalid-format"; + + // Should not find any profile (parsing should fail gracefully) + EXPECT_FALSE(srs_sdp_has_h265_profile(payload_type, "1")); + } + + // Test srs_sdp_has_h265_profile with SDP containing H.265 + if (true) { + SrsSdp sdp; + SrsMediaDesc video_desc("video"); + + SrsMediaPayloadType h265_payload(96); + h265_payload.encoding_name_ = "H265"; + h265_payload.format_specific_param_ = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST"; + video_desc.payload_types_.push_back(h265_payload); + + sdp.media_descs_.push_back(video_desc); + + // Should find Main Profile in SDP + EXPECT_TRUE(srs_sdp_has_h265_profile(sdp, "1")); + EXPECT_FALSE(srs_sdp_has_h265_profile(sdp, "2")); + } + + // Test srs_sdp_has_h265_profile with SDP containing no H.265 + if (true) { + SrsSdp sdp; + SrsMediaDesc video_desc("video"); + + SrsMediaPayloadType h264_payload(96); + h264_payload.encoding_name_ = "H264"; + h264_payload.format_specific_param_ = "profile-level-id=42e01f"; + video_desc.payload_types_.push_back(h264_payload); + + sdp.media_descs_.push_back(video_desc); + + // Should not find any H.265 profile + EXPECT_FALSE(srs_sdp_has_h265_profile(sdp, "1")); + } + + // Test srs_sdp_has_h265_profile with SDP containing audio only + if (true) { + SrsSdp sdp; + SrsMediaDesc audio_desc("audio"); + + SrsMediaPayloadType opus_payload(111); + opus_payload.encoding_name_ = "opus"; + audio_desc.payload_types_.push_back(opus_payload); + + sdp.media_descs_.push_back(audio_desc); + + // Should not find any H.265 profile in audio-only SDP + EXPECT_FALSE(srs_sdp_has_h265_profile(sdp, "1")); + } +} + +VOID TEST(KernelRTCTest, H265VideoPayload) +{ + srs_error_t err; + + // Test SrsVideoPayload::set_h265_param_desc with valid parameters + if (true) { + SrsVideoPayload payload; + string fmtp = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST"; + + HELPER_EXPECT_SUCCESS(payload.set_h265_param_desc(fmtp)); + EXPECT_STREQ("180", payload.h265_param_.level_id.c_str()); + EXPECT_STREQ("1", payload.h265_param_.profile_id.c_str()); + EXPECT_STREQ("0", payload.h265_param_.tier_flag.c_str()); + EXPECT_STREQ("SRST", payload.h265_param_.tx_mode.c_str()); + } + + // Test SrsVideoPayload::set_h265_param_desc with different order + if (true) { + SrsVideoPayload payload; + string fmtp = "tx-mode=MCTS;tier-flag=1;profile-id=2;level-id=93"; + + HELPER_EXPECT_SUCCESS(payload.set_h265_param_desc(fmtp)); + EXPECT_STREQ("93", payload.h265_param_.level_id.c_str()); + EXPECT_STREQ("2", payload.h265_param_.profile_id.c_str()); + EXPECT_STREQ("1", payload.h265_param_.tier_flag.c_str()); + EXPECT_STREQ("MCTS", payload.h265_param_.tx_mode.c_str()); + } + + // Test SrsVideoPayload::set_h265_param_desc with invalid parameter format + if (true) { + SrsVideoPayload payload; + string fmtp = "level-id=180;invalid-format;profile-id=1"; + + HELPER_EXPECT_FAILED(payload.set_h265_param_desc(fmtp)); + } + + // Test SrsVideoPayload::set_h265_param_desc with unknown parameter + if (true) { + SrsVideoPayload payload; + string fmtp = "level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST;unknown-param=value"; + + HELPER_EXPECT_FAILED(payload.set_h265_param_desc(fmtp)); + } + + // Test SrsVideoPayload::generate_media_payload_type_h265 + if (true) { + SrsVideoPayload payload; + payload.pt_ = 96; + payload.name_ = "H265"; + payload.sample_ = 90000; + payload.h265_param_.level_id = "180"; + payload.h265_param_.profile_id = "1"; + payload.h265_param_.tier_flag = "0"; + payload.h265_param_.tx_mode = "SRST"; + + SrsMediaPayloadType media_type = payload.generate_media_payload_type_h265(); + EXPECT_EQ(96, media_type.payload_type_); + EXPECT_STREQ("H265", media_type.encoding_name_.c_str()); + EXPECT_EQ(90000, media_type.clock_rate_); + EXPECT_STREQ("level-id=180;profile-id=1;tier-flag=0;tx-mode=SRST", media_type.format_specific_param_.c_str()); + } + + // Test SrsVideoPayload::generate_media_payload_type_h265 with partial parameters + if (true) { + SrsVideoPayload payload; + payload.pt_ = 97; + payload.name_ = "H265"; + payload.sample_ = 90000; + payload.h265_param_.level_id = "93"; + payload.h265_param_.profile_id = "2"; + // tier_flag and tx_mode are empty + + SrsMediaPayloadType media_type = payload.generate_media_payload_type_h265(); + EXPECT_EQ(97, media_type.payload_type_); + EXPECT_STREQ("H265", media_type.encoding_name_.c_str()); + EXPECT_EQ(90000, media_type.clock_rate_); + EXPECT_STREQ("level-id=93;profile-id=2", media_type.format_specific_param_.c_str()); + } + + // Test SrsVideoPayload::copy includes H.265 parameters + if (true) { + SrsVideoPayload original; + original.pt_ = 96; + original.name_ = "H265"; + original.sample_ = 90000; + original.h265_param_.level_id = "180"; + original.h265_param_.profile_id = "1"; + original.h265_param_.tier_flag = "0"; + original.h265_param_.tx_mode = "SRST"; + + SrsVideoPayload* copied = original.copy(); + SrsUniquePtr copied_uptr(copied); + + EXPECT_EQ(original.pt_, copied->pt_); + EXPECT_STREQ(original.name_.c_str(), copied->name_.c_str()); + EXPECT_EQ(original.sample_, copied->sample_); + EXPECT_STREQ(original.h265_param_.level_id.c_str(), copied->h265_param_.level_id.c_str()); + EXPECT_STREQ(original.h265_param_.profile_id.c_str(), copied->h265_param_.profile_id.c_str()); + EXPECT_STREQ(original.h265_param_.tier_flag.c_str(), copied->h265_param_.tier_flag.c_str()); + EXPECT_STREQ(original.h265_param_.tx_mode.c_str(), copied->h265_param_.tx_mode.c_str()); + } +} + +VOID TEST(KernelRTCTest, H265RtpSTAPPayload) +{ + srs_error_t err; + + // Test SrsRtpSTAPPayloadHevc encoding and decoding + if (true) { + SrsRtpSTAPPayloadHevc stap; + + // Create sample VPS NALU + SrsSample* vps = new SrsSample(); + uint8_t vps_data[] = {0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x3d, 0x95, 0x98, 0x09}; + vps->bytes = (char*)vps_data; + vps->size = sizeof(vps_data); + stap.nalus.push_back(vps); + + // Create sample SPS NALU + SrsSample* sps = new SrsSample(); + uint8_t sps_data[] = {0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x3d, 0xa0, 0x02, 0x80, 0x80, 0x2d, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b, 0xc0, 0x5a, 0x70, 0x80, 0x80, 0x80, 0x82}; + sps->bytes = (char*)sps_data; + sps->size = sizeof(sps_data); + stap.nalus.push_back(sps); + + // Create sample PPS NALU + SrsSample* pps = new SrsSample(); + uint8_t pps_data[] = {0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62, 0x40}; + pps->bytes = (char*)pps_data; + pps->size = sizeof(pps_data); + stap.nalus.push_back(pps); + + // Test encoding + char buf[1500]; + SrsBuffer encode_buf(buf, sizeof(buf)); + HELPER_EXPECT_SUCCESS(stap.encode(&encode_buf)); + + // Verify encoded size + uint64_t expected_size = 2 + 2 + sizeof(vps_data) + 2 + sizeof(sps_data) + 2 + sizeof(pps_data); + EXPECT_EQ(expected_size, stap.nb_bytes()); + EXPECT_EQ((int)expected_size, encode_buf.pos()); + + // Test decoding + SrsRtpSTAPPayloadHevc decode_stap; + SrsBuffer decode_buf(buf, encode_buf.pos()); // Create new buffer with encoded data + HELPER_EXPECT_SUCCESS(decode_stap.decode(&decode_buf)); + + // Verify decoded NALUs + EXPECT_EQ(3, (int)decode_stap.nalus.size()); + + // Check VPS + SrsSample* decoded_vps = decode_stap.get_vps(); + EXPECT_TRUE(decoded_vps != NULL); + EXPECT_EQ(sizeof(vps_data), (size_t)decoded_vps->size); + + // Check SPS + SrsSample* decoded_sps = decode_stap.get_sps(); + EXPECT_TRUE(decoded_sps != NULL); + EXPECT_EQ(sizeof(sps_data), (size_t)decoded_sps->size); + + // Check PPS + SrsSample* decoded_pps = decode_stap.get_pps(); + EXPECT_TRUE(decoded_pps != NULL); + EXPECT_EQ(sizeof(pps_data), (size_t)decoded_pps->size); + + // Test copy functionality + ISrsRtpPayloader* copied = stap.copy(); + SrsUniquePtr copied_uptr(copied); + SrsRtpSTAPPayloadHevc* copied_stap = dynamic_cast(copied); + EXPECT_TRUE(copied_stap != NULL); + EXPECT_EQ(3, (int)copied_stap->nalus.size()); + } + + // Test SrsRtpSTAPPayloadHevc with empty NALUs + if (true) { + SrsRtpSTAPPayloadHevc stap; + + // Test encoding with no NALUs + char buf[100]; + SrsBuffer encode_buf(buf, sizeof(buf)); + HELPER_EXPECT_SUCCESS(stap.encode(&encode_buf)); + EXPECT_EQ(2, encode_buf.pos()); // Only STAP header + + // Test get functions with no NALUs + EXPECT_TRUE(stap.get_vps() == NULL); + EXPECT_TRUE(stap.get_sps() == NULL); + EXPECT_TRUE(stap.get_pps() == NULL); + } + + // Test SrsRtpSTAPPayloadHevc decoding with forbidden_zero_bit set (should fail) + if (true) { + SrsRtpSTAPPayloadHevc stap; + + char buf[] = {static_cast(0x80), 0x01}; // forbidden_zero_bit = 1 + SrsBuffer decode_buf(buf, sizeof(buf)); + HELPER_EXPECT_FAILED(stap.decode(&decode_buf)); + } +} + +VOID TEST(KernelRTCTest, H265RtpFUAPayload) +{ + srs_error_t err; + + // Test SrsRtpFUAPayloadHevc encoding and decoding + if (true) { + SrsRtpFUAPayloadHevc fua; + fua.start = true; + fua.end = false; + fua.nalu_type = SrsHevcNaluType_CODED_SLICE_IDR; + + // Create sample payload data + SrsSample* sample = new SrsSample(); + uint8_t payload_data[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + sample->bytes = (char*)payload_data; + sample->size = sizeof(payload_data); + fua.nalus.push_back(sample); + + // Test encoding + char buf[100]; + SrsBuffer encode_buf(buf, sizeof(buf)); + HELPER_EXPECT_SUCCESS(fua.encode(&encode_buf)); + + // Verify encoded size (PayloadHdr(2) + FU header(1) + payload) + uint64_t expected_size = 3 + sizeof(payload_data); + EXPECT_EQ(expected_size, fua.nb_bytes()); + EXPECT_EQ((int)expected_size, encode_buf.pos()); + + // Test decoding + SrsRtpFUAPayloadHevc decode_fua; + SrsBuffer decode_buf(buf, encode_buf.pos()); // Create new buffer with encoded data + HELPER_EXPECT_SUCCESS(decode_fua.decode(&decode_buf)); + + // Verify decoded values + EXPECT_TRUE(decode_fua.start); + EXPECT_FALSE(decode_fua.end); + EXPECT_EQ(SrsHevcNaluType_CODED_SLICE_IDR, decode_fua.nalu_type); + EXPECT_EQ(1, (int)decode_fua.nalus.size()); + EXPECT_EQ(sizeof(payload_data), (size_t)decode_fua.nalus[0]->size); + + // Test copy functionality + ISrsRtpPayloader* copied = fua.copy(); + SrsUniquePtr copied_uptr(copied); + SrsRtpFUAPayloadHevc* copied_fua = dynamic_cast(copied); + EXPECT_TRUE(copied_fua != NULL); + EXPECT_TRUE(copied_fua->start); + EXPECT_FALSE(copied_fua->end); + EXPECT_EQ(SrsHevcNaluType_CODED_SLICE_IDR, copied_fua->nalu_type); + EXPECT_EQ(1, (int)copied_fua->nalus.size()); + } + + // Test SrsRtpFUAPayloadHevc2 encoding and decoding + if (true) { + SrsRtpFUAPayloadHevc2 fua2; + fua2.start = false; + fua2.end = true; + fua2.nalu_type = SrsHevcNaluType_CODED_SLICE_TRAIL_R; + + uint8_t payload_data[] = {0xAA, 0xBB, 0xCC, 0xDD}; + fua2.payload = (char*)payload_data; + fua2.size = sizeof(payload_data); + + // Test encoding + char buf[100]; + SrsBuffer encode_buf(buf, sizeof(buf)); + HELPER_EXPECT_SUCCESS(fua2.encode(&encode_buf)); + + // Verify encoded size (PayloadHdr(2) + FU header(1) + payload) + uint64_t expected_size = 3 + sizeof(payload_data); + EXPECT_EQ(expected_size, fua2.nb_bytes()); + EXPECT_EQ((int)expected_size, encode_buf.pos()); + + // Test decoding + SrsRtpFUAPayloadHevc2 decode_fua2; + SrsBuffer decode_buf2(buf, encode_buf.pos()); // Create new buffer with encoded data + HELPER_EXPECT_SUCCESS(decode_fua2.decode(&decode_buf2)); + + // Verify decoded values + EXPECT_FALSE(decode_fua2.start); + EXPECT_TRUE(decode_fua2.end); + EXPECT_EQ(SrsHevcNaluType_CODED_SLICE_TRAIL_R, decode_fua2.nalu_type); + EXPECT_EQ(sizeof(payload_data), (size_t)decode_fua2.size); + + // Test copy functionality + ISrsRtpPayloader* copied = fua2.copy(); + SrsUniquePtr copied_uptr(copied); + SrsRtpFUAPayloadHevc2* copied_fua2 = dynamic_cast(copied); + EXPECT_TRUE(copied_fua2 != NULL); + EXPECT_FALSE(copied_fua2->start); + EXPECT_TRUE(copied_fua2->end); + EXPECT_EQ(SrsHevcNaluType_CODED_SLICE_TRAIL_R, copied_fua2->nalu_type); + EXPECT_EQ(sizeof(payload_data), (size_t)copied_fua2->size); + } +} + +VOID TEST(KernelRTCTest, H265RtpPacketKeyframe) +{ + // Test RTP packet keyframe detection for HEVC + if (true) { + SrsRtpPacket pkt; + pkt.frame_type = SrsFrameTypeVideo; + + // Test VPS NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_VPS; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test SPS NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_SPS; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test PPS NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_PPS; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test IDR NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_CODED_SLICE_IDR; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test CRA NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_CODED_SLICE_CRA; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test BLA NALU (should be keyframe) + pkt.nalu_type = SrsHevcNaluType_CODED_SLICE_BLA; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test regular P-frame NALU (should not be keyframe) + pkt.nalu_type = SrsHevcNaluType_CODED_SLICE_TRAIL_R; + EXPECT_FALSE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test regular B-frame NALU (should not be keyframe) + pkt.nalu_type = SrsHevcNaluType_CODED_SLICE_TSA_N; + EXPECT_FALSE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + } + + // Test HEVC STAP payload keyframe detection + if (true) { + SrsRtpPacket pkt; + pkt.frame_type = SrsFrameTypeVideo; + pkt.nalu_type = kStapHevc; + + SrsRtpSTAPPayloadHevc* stap_payload = new SrsRtpSTAPPayloadHevc(); + pkt.set_payload(stap_payload, SrsRtpPacketPayloadTypeSTAPHevc); + + // Create VPS NALU + SrsSample* vps = new SrsSample(); + uint8_t vps_data[] = {0x40, 0x01}; // VPS NALU header + vps->bytes = (char*)vps_data; + vps->size = sizeof(vps_data); + stap_payload->nalus.push_back(vps); + + // Should be keyframe because it contains VPS + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + } + + // Test HEVC FU-A payload keyframe detection + if (true) { + SrsRtpPacket pkt; + pkt.frame_type = SrsFrameTypeVideo; + pkt.nalu_type = kFuHevc; + + SrsRtpFUAPayloadHevc2* fua_payload = new SrsRtpFUAPayloadHevc2(); + pkt.set_payload(fua_payload, SrsRtpPacketPayloadTypeFUAHevc2); + + // Test IDR slice in FU-A (should be keyframe) + fua_payload->nalu_type = SrsHevcNaluType_CODED_SLICE_IDR; + EXPECT_TRUE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + + // Test regular slice in FU-A (should not be keyframe) + fua_payload->nalu_type = SrsHevcNaluType_CODED_SLICE_TRAIL_R; + EXPECT_FALSE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + } + + // Test audio packet (should not be keyframe regardless of NALU type) + if (true) { + SrsRtpPacket pkt; + pkt.frame_type = SrsFrameTypeAudio; + pkt.nalu_type = SrsHevcNaluType_VPS; + EXPECT_FALSE(pkt.is_keyframe(SrsVideoCodecIdHEVC)); + } +} + +// Note: Stream bridge codec switching tests are complex and require full RTC infrastructure +// These would be better tested in integration tests rather than unit tests +VOID TEST(KernelRTCTest, H265RtpRawNALUsSkipBytes) +{ + srs_error_t err; + + // Test SrsRtpRawNALUs::skip_bytes for HEVC (2 bytes header) + if (true) { + SrsRtpRawNALUs raw_nalus; + + // Create sample HEVC NALU + SrsSample* sample = new SrsSample(); + uint8_t nalu_data[] = {0x26, 0x01, 0x12, 0x34, 0x56, 0x78}; // IDR slice + sample->bytes = (char*)nalu_data; + sample->size = sizeof(nalu_data); + raw_nalus.push_back(sample); + + // Skip HEVC header (2 bytes) + uint8_t header = raw_nalus.skip_bytes(SrsHevcNaluHeaderSize); + EXPECT_EQ(0x26, header); // Should return first byte + + // Verify remaining data + std::vector samples; + HELPER_EXPECT_SUCCESS(raw_nalus.read_samples(samples, 4)); + EXPECT_EQ(1, (int)samples.size()); + EXPECT_EQ(4, samples[0]->size); + EXPECT_EQ(0x12, (uint8_t)samples[0]->bytes[0]); + EXPECT_EQ(0x34, (uint8_t)samples[0]->bytes[1]); + EXPECT_EQ(0x56, (uint8_t)samples[0]->bytes[2]); + EXPECT_EQ(0x78, (uint8_t)samples[0]->bytes[3]); + + // Clean up + for (size_t i = 0; i < samples.size(); i++) { + srs_freep(samples[i]); + } + } + + // Test SrsRtpRawNALUs::skip_bytes for H.264 (1 byte header) + if (true) { + SrsRtpRawNALUs raw_nalus; + + // Create sample H.264 NALU + SrsSample* sample = new SrsSample(); + uint8_t nalu_data[] = {0x65, 0x12, 0x34, 0x56}; // IDR slice + sample->bytes = (char*)nalu_data; + sample->size = sizeof(nalu_data); + raw_nalus.push_back(sample); + + // Skip H.264 header (1 byte) + uint8_t header = raw_nalus.skip_bytes(SrsAvcNaluHeaderSize); + EXPECT_EQ(0x65, header); // Should return first byte + + // Verify remaining data + std::vector samples; + HELPER_EXPECT_SUCCESS(raw_nalus.read_samples(samples, 3)); + EXPECT_EQ(1, (int)samples.size()); + EXPECT_EQ(3, samples[0]->size); + EXPECT_EQ(0x12, (uint8_t)samples[0]->bytes[0]); + EXPECT_EQ(0x34, (uint8_t)samples[0]->bytes[1]); + EXPECT_EQ(0x56, (uint8_t)samples[0]->bytes[2]); + + // Clean up + for (size_t i = 0; i < samples.size(); i++) { + srs_freep(samples[i]); + } + } +} +