From feeeebf251166faabdb00bd07b1ce2f6bd1064ad Mon Sep 17 00:00:00 2001 From: Joe Turki Date: Thu, 2 Jan 2025 06:04:12 -0600 Subject: [PATCH] Upgrade golangci-lint, more linters Introduces new linters, upgrade golangci-lint to version (v1.63.4) --- .golangci.yml | 47 +- api.go | 24 +- atomicbool.go | 1 + bundlepolicy.go | 7 +- certificate.go | 38 +- configuration_common.go | 1 + configuration_test.go | 4 +- constants.go | 21 +- datachannel.go | 50 +- datachannel_go_test.go | 16 +- datachannel_test.go | 27 +- datachannelstate.go | 7 +- dtlsrole.go | 4 +- dtlsrole_test.go | 1 + dtlstransport.go | 70 ++- dtlstransport_test.go | 20 +- dtlstransportstate.go | 7 +- errors.go | 144 +++-- .../bandwidth-estimation-from-disk/main.go | 46 +- examples/broadcast/main.go | 30 +- examples/custom-logger/main.go | 38 +- examples/data-channels-detach/main.go | 42 +- examples/data-channels-flow-control/main.go | 59 +- examples/data-channels/main.go | 36 +- examples/examples.go | 18 +- examples/ice-restart/main.go | 9 +- examples/ice-single-port/main.go | 8 +- examples/ice-tcp/main.go | 9 +- examples/insertable-streams/main.go | 28 +- examples/ortc-media/main.go | 40 +- examples/ortc/main.go | 25 +- examples/pion-to-pion/answer/main.go | 65 +- examples/pion-to-pion/offer/main.go | 59 +- examples/play-from-disk-renegotiation/main.go | 51 +- examples/play-from-disk/main.go | 37 +- examples/reflect/main.go | 42 +- examples/rtcp-processing/main.go | 7 +- examples/rtp-forwarder/main.go | 65 +- examples/rtp-to-webrtc/main.go | 12 +- examples/save-to-disk-av1/main.go | 38 +- examples/save-to-disk/main.go | 46 +- examples/simulcast/main.go | 54 +- examples/stats/main.go | 23 +- examples/swap-tracks/main.go | 29 +- examples/trickle-ice/main.go | 15 +- examples/vnet/show-network-usage/main.go | 48 +- examples/whip-whep/main.go | 49 +- gathering_complete_promise.go | 6 +- icecandidate.go | 46 +- icecandidateinit.go | 2 +- icecandidatepair.go | 5 +- icecandidatetype.go | 6 +- icecomponent.go | 2 +- iceconnectionstate.go | 4 +- icecredentialtype.go | 5 +- icegatherer.go | 26 +- icegatherer_test.go | 2 +- icegathererstate.go | 2 +- icegatheringstate.go | 4 +- iceprotocol.go | 4 +- icerole.go | 7 +- iceserver.go | 22 +- iceserver_test.go | 1 + icetransport.go | 41 +- icetransportpolicy.go | 9 +- icetransportstate.go | 7 +- interceptor.go | 47 +- interceptor_test.go | 44 +- internal/fmtp/av1.go | 1 + internal/fmtp/fmtp.go | 27 +- internal/fmtp/fmtp_test.go | 2 +- internal/fmtp/h264.go | 8 +- internal/fmtp/vp9.go | 1 + internal/mux/endpoint.go | 24 +- internal/mux/mux.go | 52 +- internal/mux/mux_test.go | 48 +- internal/mux/muxfunc.go | 16 +- internal/util/util.go | 4 +- mediaengine.go | 145 +++-- mediaengine_test.go | 401 +++++++----- networktype.go | 6 +- offeransweroptions.go | 2 +- operations.go | 8 +- peerconnection.go | 582 ++++++++++++------ peerconnection_close_test.go | 4 +- peerconnection_go_test.go | 100 +-- peerconnection_media_test.go | 232 ++++--- peerconnection_renegotiation_test.go | 166 +++-- peerconnection_test.go | 28 +- peerconnectionstate.go | 2 +- pkg/media/h264reader/h264reader.go | 14 +- pkg/media/h264reader/h264reader_test.go | 4 +- pkg/media/h264reader/nalunittype.go | 13 +- pkg/media/h264writer/h264writer.go | 8 +- pkg/media/ivfreader/ivfreader.go | 14 +- pkg/media/ivfreader/ivfreader_test.go | 2 +- pkg/media/ivfwriter/ivfwriter.go | 29 +- pkg/media/ivfwriter/ivfwriter_test.go | 9 +- pkg/media/media.go | 4 +- pkg/media/oggreader/oggreader.go | 38 +- pkg/media/oggreader/oggreader_test.go | 2 +- pkg/media/oggwriter/oggwriter.go | 48 +- pkg/media/rtpdump/reader.go | 16 +- pkg/media/rtpdump/reader_test.go | 8 +- pkg/media/rtpdump/rtpdump.go | 42 +- pkg/media/rtpdump/rtpdump_test.go | 4 +- pkg/media/rtpdump/writer.go | 4 +- .../samplebuilder/sampleSequenceLocation.go | 1 + pkg/media/samplebuilder/samplebuilder.go | 47 +- pkg/media/samplebuilder/samplebuilder_test.go | 295 +++++---- pkg/null/null.go | 72 +-- rtcpmuxpolicy.go | 7 +- rtpcodec.go | 20 +- rtpreceiveparameters.go | 2 +- rtpreceiver.go | 124 +++- rtpreceiver_go_test.go | 26 +- rtpreceiver_test.go | 2 +- rtpsender.go | 61 +- rtpsender_test.go | 54 +- rtpsendparameters.go | 2 +- rtptransceiver.go | 59 +- rtptransceiver_test.go | 26 +- rtptransceiverdirection.go | 10 +- rtptransceiverinit.go | 3 +- rtptransceiverinit_go_test.go | 4 +- sctptransport.go | 22 +- sctptransport_test.go | 13 +- sctptransportstate.go | 2 +- sdp.go | 281 ++++++--- sdp_test.go | 258 ++++++-- sdpsemantics.go | 9 +- sdpsemantics_test.go | 29 +- sdptype.go | 8 +- sessiondescription.go | 3 +- sessiondescription_test.go | 1 + settingengine.go | 59 +- settingengine_test.go | 156 +++-- signalingstate.go | 10 +- srtp_writer_future.go | 5 +- stats.go | 55 +- stats_go.go | 18 +- stats_go_test.go | 54 +- track_local.go | 18 +- track_local_static.go | 90 +-- track_local_static_test.go | 93 ++- track_remote.go | 48 +- vnet_test.go | 7 +- 147 files changed, 3842 insertions(+), 2139 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a3235bec..88cb4fbf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,17 +25,32 @@ linters-settings: - ^os.Exit$ - ^panic$ - ^print(ln)?$ + varnamelen: + max-distance: 12 + min-name-length: 2 + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-decls: + - i int + - n int + - w io.Writer + - r io.Reader + - b []byte linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - containedctx # containedctx is a linter that detects struct contained context.Context field - contextcheck # check the function whether use a non-inherited context + - cyclop # checks function and package cyclomatic complexity - decorder # check declaration order and count of types, constants, variables and functions - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection - durationcheck # check for two durations multiplied together + - err113 # Golang linter to check the errors handling expressions - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. @@ -46,18 +61,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 +79,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 +95,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 +118,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! diff --git a/api.go b/api.go index 369b1a9a..a96ebad4 100644 --- a/api.go +++ b/api.go @@ -29,38 +29,38 @@ type API struct { // It uses the default Codecs and Interceptors unless you customize them // using WithMediaEngine and WithInterceptorRegistry respectively. func NewAPI(options ...func(*API)) *API { - a := &API{ + api := &API{ interceptor: &interceptor.NoOp{}, settingEngine: &SettingEngine{}, } for _, o := range options { - o(a) + o(api) } - if a.settingEngine.LoggerFactory == nil { - a.settingEngine.LoggerFactory = logging.NewDefaultLoggerFactory() + if api.settingEngine.LoggerFactory == nil { + api.settingEngine.LoggerFactory = logging.NewDefaultLoggerFactory() } - logger := a.settingEngine.LoggerFactory.NewLogger("api") + logger := api.settingEngine.LoggerFactory.NewLogger("api") - if a.mediaEngine == nil { - a.mediaEngine = &MediaEngine{} - err := a.mediaEngine.RegisterDefaultCodecs() + if api.mediaEngine == nil { + api.mediaEngine = &MediaEngine{} + err := api.mediaEngine.RegisterDefaultCodecs() if err != nil { logger.Errorf("Failed to register default codecs %s", err) } } - if a.interceptorRegistry == nil { - a.interceptorRegistry = &interceptor.Registry{} - err := RegisterDefaultInterceptors(a.mediaEngine, a.interceptorRegistry) + if api.interceptorRegistry == nil { + api.interceptorRegistry = &interceptor.Registry{} + err := RegisterDefaultInterceptors(api.mediaEngine, api.interceptorRegistry) if err != nil { logger.Errorf("Failed to register default interceptors %s", err) } } - return a + return api } // WithMediaEngine allows providing a MediaEngine to the API. diff --git a/atomicbool.go b/atomicbool.go index cc6cdc1e..846289ec 100644 --- a/atomicbool.go +++ b/atomicbool.go @@ -27,5 +27,6 @@ func (b *atomicBool) swap(value bool) bool { if value { i = 1 } + return atomic.SwapInt32(&(b.val), i) != 0 } diff --git a/bundlepolicy.go b/bundlepolicy.go index 7e3d6c50..1750ade3 100644 --- a/bundlepolicy.go +++ b/bundlepolicy.go @@ -14,7 +14,7 @@ import ( type BundlePolicy int const ( - // BundlePolicyUnknown is the enum's zero-value + // BundlePolicyUnknown is the enum's zero-value. BundlePolicyUnknown BundlePolicy = iota // BundlePolicyBalanced indicates to gather ICE candidates for each @@ -67,7 +67,7 @@ func (t BundlePolicy) String() string { } } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (t *BundlePolicy) UnmarshalJSON(b []byte) error { var val string if err := json.Unmarshal(b, &val); err != nil { @@ -75,10 +75,11 @@ func (t *BundlePolicy) UnmarshalJSON(b []byte) error { } *t = newBundlePolicy(val) + return nil } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (t BundlePolicy) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } diff --git a/certificate.go b/certificate.go index 36d1fb07..dfceb9b1 100644 --- a/certificate.go +++ b/certificate.go @@ -62,28 +62,36 @@ func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certificate, return nil, &rtcerr.UnknownError{Err: err} } - return &Certificate{privateKey: key, x509Cert: cert, statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano())}, nil + return &Certificate{ + privateKey: key, + x509Cert: cert, + statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano()), + }, nil } // Equals determines if two certificates are identical by comparing both the // secretKeys and x509Certificates. -func (c Certificate) Equals(o Certificate) bool { +func (c Certificate) Equals(cert Certificate) bool { switch cSK := c.privateKey.(type) { case *rsa.PrivateKey: - if oSK, ok := o.privateKey.(*rsa.PrivateKey); ok { + if oSK, ok := cert.privateKey.(*rsa.PrivateKey); ok { if cSK.N.Cmp(oSK.N) != 0 { return false } - return c.x509Cert.Equal(o.x509Cert) + + return c.x509Cert.Equal(cert.x509Cert) } + return false case *ecdsa.PrivateKey: - if oSK, ok := o.privateKey.(*ecdsa.PrivateKey); ok { + if oSK, ok := cert.privateKey.(*ecdsa.PrivateKey); ok { if cSK.X.Cmp(oSK.X) != 0 || cSK.Y.Cmp(oSK.Y) != 0 { return false } - return c.x509Cert.Equal(o.x509Cert) + + return c.x509Cert.Equal(cert.x509Cert) } + return false default: return false @@ -95,6 +103,7 @@ func (c Certificate) Expires() time.Time { if c.x509Cert == nil { return time.Time{} } + return c.x509Cert.NotAfter } @@ -150,7 +159,7 @@ func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, error) { // CertificateFromX509 creates a new WebRTC Certificate from a given PrivateKey and Certificate // -// This can be used if you want to share a certificate across multiple PeerConnections +// This can be used if you want to share a certificate across multiple PeerConnections. func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509.Certificate) Certificate { return Certificate{privateKey, certificate, fmt.Sprintf("certificate-%d", time.Now().UnixNano())} } @@ -176,11 +185,12 @@ func (c Certificate) collectStats(report *statsReportCollector) error { } report.Collect(stats.ID, stats) + return nil } // CertificateFromPEM creates a fresh certificate based on a string containing -// pem blocks fort the private key and x509 certificate +// pem blocks fort the private key and x509 certificate. func CertificateFromPEM(pems string) (*Certificate, error) { // decode & parse the certificate block, more := pem.Decode([]byte(pems)) @@ -206,18 +216,19 @@ func CertificateFromPEM(pems string) (*Certificate, error) { return nil, fmt.Errorf("unable to parse private key: %w", err) } x := CertificateFromX509(privateKey, cert) + return &x, nil } // PEM returns the certificate encoded as two pem block: once for the X509 -// certificate and the other for the private key +// certificate and the other for the private key. func (c Certificate) PEM() (string, error) { // First write the X509 certificate - var o strings.Builder + var builder strings.Builder xcertBytes := make( []byte, base64.StdEncoding.EncodedLen(len(c.x509Cert.Raw))) base64.StdEncoding.Encode(xcertBytes, c.x509Cert.Raw) - err := pem.Encode(&o, &pem.Block{Type: "CERTIFICATE", Bytes: xcertBytes}) + err := pem.Encode(&builder, &pem.Block{Type: "CERTIFICATE", Bytes: xcertBytes}) if err != nil { return "", fmt.Errorf("failed to pem encode the X certificate: %w", err) } @@ -226,9 +237,10 @@ func (c Certificate) PEM() (string, error) { if err != nil { return "", fmt.Errorf("failed to marshal private key: %w", err) } - err = pem.Encode(&o, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + err = pem.Encode(&builder, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) if err != nil { return "", fmt.Errorf("failed to encode private key: %w", err) } - return o.String(), nil + + return builder.String(), nil } diff --git a/configuration_common.go b/configuration_common.go index a3acdf5b..4fb22fb8 100644 --- a/configuration_common.go +++ b/configuration_common.go @@ -23,5 +23,6 @@ func (c Configuration) getICEServers() []ICEServer { iceServers[iceServersIndex].URLs[urlsIndex] = rawURL } } + return iceServers } diff --git a/configuration_test.go b/configuration_test.go index 06fd35b3..f1b69f9b 100644 --- a/configuration_test.go +++ b/configuration_test.go @@ -43,7 +43,7 @@ func TestConfiguration_getICEServers(t *testing.T) { } func TestConfigurationJSON(t *testing.T) { - j := `{ + config := `{ "iceServers": [{"urls": ["turn:turn.example.org"], "username": "jch", "credential": "topsecret" @@ -67,7 +67,7 @@ func TestConfigurationJSON(t *testing.T) { } var conf2 Configuration - assert.NoError(t, json.Unmarshal([]byte(j), &conf2)) + assert.NoError(t, json.Unmarshal([]byte(config), &conf2)) assert.Equal(t, conf, conf2) j2, err := json.Marshal(conf2) diff --git a/constants.go b/constants.go index 5a22e339..afcf5bfa 100644 --- a/constants.go +++ b/constants.go @@ -7,16 +7,16 @@ import "github.com/pion/dtls/v3" const ( // default as the standard ethernet MTU - // can be overwritten with SettingEngine.SetReceiveMTU() + // can be overwritten with SettingEngine.SetReceiveMTU(). receiveMTU = 1500 // simulcastProbeCount is the amount of RTP Packets // that handleUndeclaredSSRC will read and try to dispatch from - // mid and rid values + // mid and rid values. simulcastProbeCount = 10 // simulcastMaxProbeRoutines is how many active routines can be used to probe - // If the total amount of incoming SSRCes exceeds this new requests will be ignored + // If the total amount of incoming SSRCes exceeds this new requests will be ignored. simulcastMaxProbeRoutines = 25 mediaSectionApplication = "application" @@ -33,14 +33,21 @@ const ( generatedCertificateOrigin = "WebRTC" - // AttributeRtxPayloadType is the interceptor attribute added when Read() returns an RTX packet containing the RTX stream payload type + // AttributeRtxPayloadType is the interceptor attribute added when Read() + // returns an RTX packet containing the RTX stream payload type. AttributeRtxPayloadType = "rtx_payload_type" - // AttributeRtxSsrc is the interceptor attribute added when Read() returns an RTX packet containing the RTX stream SSRC + // AttributeRtxSsrc is the interceptor attribute added when Read() + // returns an RTX packet containing the RTX stream SSRC. AttributeRtxSsrc = "rtx_ssrc" - // AttributeRtxSequenceNumber is the interceptor attribute added when Read() returns an RTX packet containing the RTX stream sequence number + // AttributeRtxSequenceNumber is the interceptor attribute added when + // Read() returns an RTX packet containing the RTX stream sequence number. AttributeRtxSequenceNumber = "rtx_sequence_number" ) func defaultSrtpProtectionProfiles() []dtls.SRTPProtectionProfile { - return []dtls.SRTPProtectionProfile{dtls.SRTP_AEAD_AES_256_GCM, dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80} + return []dtls.SRTPProtectionProfile{ + dtls.SRTP_AEAD_AES_256_GCM, + dtls.SRTP_AEAD_AES_128_GCM, + dtls.SRTP_AES128_CM_HMAC_SHA1_80, + } } diff --git a/datachannel.go b/datachannel.go index ea50a031..4c3d63f7 100644 --- a/datachannel.go +++ b/datachannel.go @@ -25,7 +25,7 @@ var errSCTPNotEstablished = errors.New("SCTP not established") // DataChannel represents a WebRTC DataChannel // The DataChannel interface represents a network channel -// which can be used for bidirectional peer-to-peer transfers of arbitrary data +// which can be used for bidirectional peer-to-peer transfers of arbitrary data. type DataChannel struct { mu sync.RWMutex @@ -87,13 +87,17 @@ func (api *API) NewDataChannel(transport *SCTPTransport, params *DataChannelPara // newDataChannel is an internal constructor for the data channel used to // create the DataChannel object before the networking is set up. -func (api *API) newDataChannel(params *DataChannelParameters, sctpTransport *SCTPTransport, log logging.LeveledLogger) (*DataChannel, error) { +func (api *API) newDataChannel( + params *DataChannelParameters, + sctpTransport *SCTPTransport, + log logging.LeveledLogger, +) (*DataChannel, error) { // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5) if len(params.Label) > 65535 { return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit} } - d := &DataChannel{ + dataChannel := &DataChannel{ sctpTransport: sctpTransport, statsID: fmt.Sprintf("DataChannel-%d", time.Now().UnixNano()), label: params.Label, @@ -107,12 +111,13 @@ func (api *API) newDataChannel(params *DataChannelParameters, sctpTransport *SCT log: log, } - d.setReadyState(DataChannelStateConnecting) - return d, nil + dataChannel.setReadyState(DataChannelStateConnecting) + + return dataChannel, nil } -// open opens the datachannel over the sctp transport -func (d *DataChannel) open(sctpTransport *SCTPTransport) error { +// open opens the datachannel over the sctp transport. +func (d *DataChannel) open(sctpTransport *SCTPTransport) error { //nolint:cyclop association := sctpTransport.association() if association == nil { return errSCTPNotEstablished @@ -121,6 +126,7 @@ func (d *DataChannel) open(sctpTransport *SCTPTransport) error { d.mu.Lock() if d.sctpTransport != nil { // already open d.mu.Unlock() + return nil } d.sctpTransport = sctpTransport @@ -175,6 +181,7 @@ func (d *DataChannel) open(sctpTransport *SCTPTransport) error { dc, err := datachannel.Dial(association, *d.id, cfg) if err != nil { d.mu.Unlock() + return err } @@ -185,6 +192,7 @@ func (d *DataChannel) open(sctpTransport *SCTPTransport) error { d.onDial() d.handleOpen(dc, false, d.negotiated) + return nil } @@ -197,7 +205,7 @@ func (d *DataChannel) Transport() *SCTPTransport { } // After onOpen is complete check that the user called detach -// and provide an error message if the call was missed +// and provide an error message if the call was missed. func (d *DataChannel) checkDetachAfterOpen() { d.mu.RLock() defer d.mu.RUnlock() @@ -229,6 +237,7 @@ func (d *DataChannel) onOpen() { handler := d.onOpenHandler if d.isGracefulClosed { d.mu.RUnlock() + return } d.mu.RUnlock() @@ -242,7 +251,7 @@ func (d *DataChannel) onOpen() { } // OnDial sets an event handler which is invoked when the -// peer has been dialed, but before said peer has responded +// peer has been dialed, but before said peer has responded. func (d *DataChannel) OnDial(f func()) { d.mu.Lock() d.dialHandlerOnce = sync.Once{} @@ -260,6 +269,7 @@ func (d *DataChannel) onDial() { handler := d.onDialHandler if d.isGracefulClosed { d.mu.RUnlock() + return } d.mu.RUnlock() @@ -308,6 +318,7 @@ func (d *DataChannel) onMessage(msg DataChannelMessage) { handler := d.onMessageHandler if d.isGracefulClosed { d.mu.RUnlock() + return } d.mu.RUnlock() @@ -376,6 +387,7 @@ func (d *DataChannel) onError(err error) { handler := d.onErrorHandler if d.isGracefulClosed { d.mu.RUnlock() + return } d.mu.RUnlock() @@ -401,18 +413,19 @@ func (d *DataChannel) readLoop() { d.onError(err) } d.onClose() + return } - m := DataChannelMessage{Data: make([]byte, n), IsString: isString} - copy(m.Data, buffer[:n]) + msg := DataChannelMessage{Data: make([]byte, n), IsString: isString} + copy(msg.Data, buffer[:n]) // NB: Why was DataChannelMessage not passed as a pointer value? - d.onMessage(m) // nolint:staticcheck + d.onMessage(msg) // nolint:staticcheck } } -// Send sends the binary message to the DataChannel peer +// Send sends the binary message to the DataChannel peer. func (d *DataChannel) Send(data []byte) error { err := d.ensureOpen() if err != nil { @@ -420,10 +433,11 @@ func (d *DataChannel) Send(data []byte) error { } _, err = d.dataChannel.WriteDataChannel(data, false) + return err } -// SendText sends the text message to the DataChannel peer +// SendText sends the text message to the DataChannel peer. func (d *DataChannel) SendText(s string) error { err := d.ensureOpen() if err != nil { @@ -431,6 +445,7 @@ func (d *DataChannel) SendText(s string) error { } _, err = d.dataChannel.WriteDataChannel([]byte(s), true) + return err } @@ -440,6 +455,7 @@ func (d *DataChannel) ensureOpen() error { if d.ReadyState() != DataChannelStateOpen { return io.ErrClosedPipe } + return nil } @@ -465,11 +481,13 @@ func (d *DataChannel) DetachWithDeadline() (datachannel.ReadWriteCloserDeadliner if !d.api.settingEngine.detach.DataChannels { d.mu.Unlock() + return nil, errDetachNotEnabled } if d.dataChannel == nil { d.mu.Unlock() + return nil, errDetachBeforeOpened } @@ -616,6 +634,7 @@ func (d *DataChannel) ReadyState() DataChannelState { if v, ok := d.readyState.Load().(DataChannelState); ok { return v } + return DataChannelState(0) } @@ -636,6 +655,7 @@ func (d *DataChannel) BufferedAmount() uint64 { if d.dataChannel == nil { return 0 } + return d.dataChannel.BufferedAmount() } @@ -652,6 +672,7 @@ func (d *DataChannel) BufferedAmountLowThreshold() uint64 { if d.dataChannel == nil { return d.bufferedAmountLowThreshold } + return d.dataChannel.BufferedAmountLowThreshold() } @@ -684,6 +705,7 @@ func (d *DataChannel) OnBufferedAmountLow(f func()) { func (d *DataChannel) getStatsID() string { d.mu.Lock() defer d.mu.Unlock() + return d.statsID } diff --git a/datachannel_go_test.go b/datachannel_go_test.go index bee388f0..60c63344 100644 --- a/datachannel_go_test.go +++ b/datachannel_go_test.go @@ -180,7 +180,7 @@ func TestDataChannelParamters_Go(t *testing.T) { }) } -func TestDataChannelBufferedAmount(t *testing.T) { +func TestDataChannelBufferedAmount(t *testing.T) { //nolint:cyclop t.Run("set before datachannel becomes open", func(t *testing.T) { report := test.CheckRoutines(t) defer report() @@ -301,14 +301,14 @@ func TestDataChannelBufferedAmount(t *testing.T) { done := make(chan bool) - answerPC.OnDataChannel(func(d *DataChannel) { + answerPC.OnDataChannel(func(dataChannel *DataChannel) { // Make sure this is the data channel we were looking for. (Not the one // created in signalPair). - if d.Label() != expectedLabel { + if dataChannel.Label() != expectedLabel { return } var nPacketsReceived int - d.OnMessage(func(DataChannelMessage) { + dataChannel.OnMessage(func(DataChannelMessage) { nPacketsReceived++ if nPacketsReceived == 10 { @@ -318,7 +318,7 @@ func TestDataChannelBufferedAmount(t *testing.T) { }() } }) - assert.True(t, d.Ordered(), "Ordered should be set to true") + assert.True(t, dataChannel.Ordered(), "Ordered should be set to true") }) dc, err := offerPC.CreateDataChannel(expectedLabel, nil) @@ -360,7 +360,9 @@ func TestDataChannelBufferedAmount(t *testing.T) { }) } -func TestEOF(t *testing.T) { +func TestEOF(t *testing.T) { //nolint:cyclop + t.Helper() + report := test.CheckRoutines(t) defer report() @@ -566,7 +568,7 @@ func TestEOF(t *testing.T) { } // Assert that a Session Description that doesn't follow -// draft-ietf-mmusic-sctp-sdp is still accepted +// draft-ietf-mmusic-sctp-sdp is still accepted. func TestDataChannel_NonStandardSessionDescription(t *testing.T) { to := test.TimeOut(time.Second * 20) defer to.Stop() diff --git a/datachannel_test.go b/datachannel_test.go index 5969da81..82c638c8 100644 --- a/datachannel_test.go +++ b/datachannel_test.go @@ -19,22 +19,26 @@ import ( // bindings this is a requirement). const expectedLabel = "data" -func closePairNow(t testing.TB, pc1, pc2 io.Closer) { +func closePairNow(tb testing.TB, pc1, pc2 io.Closer) { + tb.Helper() + var fail bool if err := pc1.Close(); err != nil { - t.Errorf("Failed to close PeerConnection: %v", err) + tb.Errorf("Failed to close PeerConnection: %v", err) fail = true } if err := pc2.Close(); err != nil { - t.Errorf("Failed to close PeerConnection: %v", err) + tb.Errorf("Failed to close PeerConnection: %v", err) fail = true } if fail { - t.FailNow() + tb.FailNow() } } func closePair(t *testing.T, pc1, pc2 io.Closer, done <-chan bool) { + t.Helper() + select { case <-time.After(10 * time.Second): t.Fatalf("closePair timed out waiting for done signal") @@ -43,7 +47,12 @@ func closePair(t *testing.T, pc1, pc2 io.Closer, done <-chan bool) { } } -func setUpDataChannelParametersTest(t *testing.T, options *DataChannelInit) (*PeerConnection, *PeerConnection, *DataChannel, chan bool) { +func setUpDataChannelParametersTest( + t *testing.T, + options *DataChannelInit, +) (*PeerConnection, *PeerConnection, *DataChannel, chan bool) { + t.Helper() + offerPC, answerPC, err := newPair() if err != nil { t.Fatalf("Failed to create a PC pair for testing") @@ -59,6 +68,8 @@ func setUpDataChannelParametersTest(t *testing.T, options *DataChannelInit) (*Pe } func closeReliabilityParamTest(t *testing.T, pc1, pc2 *PeerConnection, done chan bool) { + t.Helper() + err := signalPair(pc1, pc2) if err != nil { t.Fatalf("Failed to signal our PC pair for testing") @@ -75,6 +86,8 @@ func BenchmarkDataChannelSend32(b *testing.B) { benchmarkDataChannelSend(b, 32) // See https://github.com/pion/webrtc/issues/1516 func benchmarkDataChannelSend(b *testing.B, numChannels int) { + b.Helper() + offerPC, answerPC, err := newPair() if err != nil { b.Fatalf("Failed to create a PC pair for testing") @@ -219,7 +232,7 @@ func TestDataChannel_Open(t *testing.T) { }) } -func TestDataChannel_Send(t *testing.T) { +func TestDataChannel_Send(t *testing.T) { //nolint:cyclop t.Run("before signaling", func(t *testing.T) { report := test.CheckRoutines(t) defer report() @@ -362,7 +375,7 @@ func TestDataChannel_Close(t *testing.T) { }) } -func TestDataChannelParameters(t *testing.T) { +func TestDataChannelParameters(t *testing.T) { //nolint:cyclop report := test.CheckRoutines(t) defer report() diff --git a/datachannelstate.go b/datachannelstate.go index 848d94ca..ad275c5d 100644 --- a/datachannelstate.go +++ b/datachannelstate.go @@ -7,7 +7,7 @@ package webrtc type DataChannelState int const ( - // DataChannelStateUnknown is the enum's zero-value + // DataChannelStateUnknown is the enum's zero-value. DataChannelStateUnknown DataChannelState = iota // DataChannelStateConnecting indicates that the data channel is being @@ -66,13 +66,14 @@ func (t DataChannelState) String() string { } } -// MarshalText implements encoding.TextMarshaler +// MarshalText implements encoding.TextMarshaler. func (t DataChannelState) MarshalText() ([]byte, error) { return []byte(t.String()), nil } -// UnmarshalText implements encoding.TextUnmarshaler +// UnmarshalText implements encoding.TextUnmarshaler. func (t *DataChannelState) UnmarshalText(b []byte) error { *t = newDataChannelState(string(b)) + return nil } diff --git a/dtlsrole.go b/dtlsrole.go index 40a14e87..94cbac96 100644 --- a/dtlsrole.go +++ b/dtlsrole.go @@ -11,7 +11,7 @@ import ( type DTLSRole byte const ( - // DTLSRoleUnknown is the enum's zero-value + // DTLSRoleUnknown is the enum's zero-value. DTLSRoleUnknown DTLSRole = iota // DTLSRoleAuto defines the DTLS role is determined based on @@ -60,7 +60,7 @@ func (r DTLSRole) String() string { // Iterate a SessionDescription from a remote to determine if an explicit // role can been determined from it. The decision is made from the first role we we parse. -// If no role can be found we return DTLSRoleAuto +// If no role can be found we return DTLSRoleAuto. func dtlsRoleFromRemoteSDP(sessionDescription *sdp.SessionDescription) DTLSRole { if sessionDescription == nil { return DTLSRoleAuto diff --git a/dtlsrole_test.go b/dtlsrole_test.go index c709f31d..5639908e 100644 --- a/dtlsrole_test.go +++ b/dtlsrole_test.go @@ -37,6 +37,7 @@ func TestDTLSRoleFromRemoteSDP(t *testing.T) { if err := parsed.Unmarshal([]byte(raw)); err != nil { panic(err) } + return parsed } diff --git a/dtlstransport.go b/dtlstransport.go index 3e442923..e0b575a1 100644 --- a/dtlstransport.go +++ b/dtlstransport.go @@ -69,7 +69,7 @@ type simulcastStreamPair struct { // This constructor is part of the ORTC API. It is not // meant to be used together with the basic WebRTC API. func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certificate) (*DTLSTransport, error) { - t := &DTLSTransport{ + trans := &DTLSTransport{ iceTransport: transport, api: api, state: DTLSTransportStateNew, @@ -84,7 +84,7 @@ func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certifi if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) { return nil, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired} } - t.certificates = append(t.certificates, x509Cert) + trans.certificates = append(trans.certificates, x509Cert) } } else { sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -95,21 +95,22 @@ func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certifi if err != nil { return nil, err } - t.certificates = []Certificate{*certificate} + trans.certificates = []Certificate{*certificate} } - return t, nil + return trans, nil } // ICETransport returns the currently-configured *ICETransport or nil -// if one has not been configured +// if one has not been configured. func (t *DTLSTransport) ICETransport() *ICETransport { t.lock.RLock() defer t.lock.RUnlock() + return t.iceTransport } -// onStateChange requires the caller holds the lock +// onStateChange requires the caller holds the lock. func (t *DTLSTransport) onStateChange(state DTLSTransportState) { t.state = state handler := t.onStateChangeHandler @@ -130,6 +131,7 @@ func (t *DTLSTransport) OnStateChange(f func(DTLSTransportState)) { func (t *DTLSTransport) State() DTLSTransportState { t.lock.RLock() defer t.lock.RUnlock() + return t.state } @@ -175,10 +177,11 @@ func (t *DTLSTransport) GetLocalParameters() (DTLSParameters, error) { } // GetRemoteCertificate returns the certificate chain in use by the remote side -// returns an empty list prior to selection of the remote certificate +// returns an empty list prior to selection of the remote certificate. func (t *DTLSTransport) GetRemoteCertificate() []byte { t.lock.RLock() defer t.lock.RUnlock() + return t.remoteCertificate } @@ -243,6 +246,7 @@ func (t *DTLSTransport) startSRTP() error { t.srtpSession.Store(srtpSession) t.srtcpSession.Store(srtcpSession) close(t.srtpReady) + return nil } @@ -285,11 +289,12 @@ func (t *DTLSTransport) role() DTLSRole { if t.iceTransport.Role() == ICERoleControlling { return DTLSRoleServer } + return defaultDtlsRoleAnswer } -// Start DTLS transport negotiation with the parameters of the remote DTLS transport -func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: gocognit +// Start DTLS transport negotiation with the parameters of the remote DTLS transport. +func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint:gocognit,cyclop // Take lock and prepare connection, we must not hold the lock // when connecting prepareTransport := func() (DTLSRole, *dtls.Config, error) { @@ -341,7 +346,7 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: } if t.api.settingEngine.replayProtection.DTLS != nil { - dtlsConfig.ReplayProtectionWindow = int(*t.api.settingEngine.replayProtection.DTLS) + dtlsConfig.ReplayProtectionWindow = int(*t.api.settingEngine.replayProtection.DTLS) //nolint:gosec // G115 } if t.api.settingEngine.dtls.clientAuth != nil { @@ -382,12 +387,14 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: if err != nil { t.onStateChange(DTLSTransportStateFailed) + return err } srtpProfile, ok := dtlsConn.SelectedSRTPProtectionProfile() if !ok { t.onStateChange(DTLSTransportStateFailed) + return ErrNoSRTPProtectionProfile } @@ -402,6 +409,7 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: t.srtpProtectionProfile = srtp.ProtectionProfileNullHmacSha1_80 default: t.onStateChange(DTLSTransportStateFailed) + return ErrNoSRTPProtectionProfile } @@ -409,16 +417,18 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: connectionState, ok := dtlsConn.ConnectionState() if !ok { t.onStateChange(DTLSTransportStateFailed) + return errNoRemoteCertificate } if len(connectionState.PeerCertificates) == 0 { t.onStateChange(DTLSTransportStateFailed) + return errNoRemoteCertificate } t.remoteCertificate = connectionState.PeerCertificates[0] - if !t.api.settingEngine.disableCertificateFingerprintVerification { + if !t.api.settingEngine.disableCertificateFingerprintVerification { //nolint:nestif parsedRemoteCert, err := x509.ParseCertificate(t.remoteCertificate) if err != nil { if closeErr := dtlsConn.Close(); closeErr != nil { @@ -426,6 +436,7 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: } t.onStateChange(DTLSTransportStateFailed) + return err } @@ -435,6 +446,7 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { //nolint: } t.onStateChange(DTLSTransportStateFailed) + return err } } @@ -473,6 +485,7 @@ func (t *DTLSTransport) Stop() error { } } t.onStateChange(DTLSTransportStateClosed) + return util.FlattenErrs(closeErrs) } @@ -504,14 +517,20 @@ func (t *DTLSTransport) ensureICEConn() error { return nil } -func (t *DTLSTransport) storeSimulcastStream(srtpReadStream *srtp.ReadStreamSRTP, srtcpReadStream *srtp.ReadStreamSRTCP) { +func (t *DTLSTransport) storeSimulcastStream( + srtpReadStream *srtp.ReadStreamSRTP, + srtcpReadStream *srtp.ReadStreamSRTCP, +) { t.lock.Lock() defer t.lock.Unlock() t.simulcastStreams = append(t.simulcastStreams, simulcastStreamPair{srtpReadStream, srtcpReadStream}) } -func (t *DTLSTransport) streamsForSSRC(ssrc SSRC, streamInfo interceptor.StreamInfo) (*srtp.ReadStreamSRTP, interceptor.RTPReader, *srtp.ReadStreamSRTCP, interceptor.RTCPReader, error) { +func (t *DTLSTransport) streamsForSSRC( + ssrc SSRC, + streamInfo interceptor.StreamInfo, +) (*srtp.ReadStreamSRTP, interceptor.RTPReader, *srtp.ReadStreamSRTCP, interceptor.RTCPReader, error) { srtpSession, err := t.getSRTPSession() if err != nil { return nil, nil, nil, nil, err @@ -522,10 +541,16 @@ func (t *DTLSTransport) streamsForSSRC(ssrc SSRC, streamInfo interceptor.StreamI return nil, nil, nil, nil, err } - rtpInterceptor := t.api.interceptor.BindRemoteStream(&streamInfo, interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { - n, err = rtpReadStream.Read(in) - return n, a, err - })) + rtpInterceptor := t.api.interceptor.BindRemoteStream( + &streamInfo, + interceptor.RTPReaderFunc( + func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { + n, err = rtpReadStream.Read(in) + + return n, a, err + }, + ), + ) srtcpSession, err := t.getSRTCPSession() if err != nil { @@ -537,10 +562,13 @@ func (t *DTLSTransport) streamsForSSRC(ssrc SSRC, streamInfo interceptor.StreamI return nil, nil, nil, nil, err } - rtcpInterceptor := t.api.interceptor.BindRTCPReader(interceptor.RTCPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { - n, err = rtcpReadStream.Read(in) - return n, a, err - })) + rtcpInterceptor := t.api.interceptor.BindRTCPReader(interceptor.RTCPReaderFunc( + func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { + n, err = rtcpReadStream.Read(in) + + return n, a, err + }), + ) return rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor, nil } diff --git a/dtlstransport_test.go b/dtlstransport_test.go index bc3dc552..afffcf19 100644 --- a/dtlstransport_test.go +++ b/dtlstransport_test.go @@ -15,8 +15,8 @@ import ( "github.com/stretchr/testify/assert" ) -// An invalid fingerprint MUST cause PeerConnectionState to go to PeerConnectionStateFailed -func TestInvalidFingerprintCausesFailed(t *testing.T) { +// An invalid fingerprint MUST cause PeerConnectionState to go to PeerConnectionStateFailed. +func TestInvalidFingerprintCausesFailed(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -64,7 +64,10 @@ func TestInvalidFingerprintCausesFailed(t *testing.T) { case offer := <-offerChan: // Replace with invalid fingerprint re := regexp.MustCompile(`sha-256 (.*?)\r`) - offer.SDP = re.ReplaceAllString(offer.SDP, "sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r") + offer.SDP = re.ReplaceAllString( + offer.SDP, + "sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r", + ) if err := pcAnswer.SetRemoteDescription(offer); err != nil { t.Fatal(err) @@ -79,7 +82,10 @@ func TestInvalidFingerprintCausesFailed(t *testing.T) { t.Fatal(err) } - answer.SDP = re.ReplaceAllString(answer.SDP, "sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r") + answer.SDP = re.ReplaceAllString( + answer.SDP, + "sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r", + ) err = pcOffer.SetRemoteDescription(answer) if err != nil { @@ -92,12 +98,14 @@ func TestInvalidFingerprintCausesFailed(t *testing.T) { offerConnectionHasClosed.Wait() answerConnectionHasClosed.Wait() - if pcOffer.SCTP().Transport().State() != DTLSTransportStateClosed && pcOffer.SCTP().Transport().State() != DTLSTransportStateFailed { + if pcOffer.SCTP().Transport().State() != DTLSTransportStateClosed && + pcOffer.SCTP().Transport().State() != DTLSTransportStateFailed { t.Fail() } assert.Nil(t, pcOffer.SCTP().Transport().conn) - if pcAnswer.SCTP().Transport().State() != DTLSTransportStateClosed && pcAnswer.SCTP().Transport().State() != DTLSTransportStateFailed { + if pcAnswer.SCTP().Transport().State() != DTLSTransportStateClosed && + pcAnswer.SCTP().Transport().State() != DTLSTransportStateFailed { t.Fail() } assert.Nil(t, pcAnswer.SCTP().Transport().conn) diff --git a/dtlstransportstate.go b/dtlstransportstate.go index c938ae2c..933e38dc 100644 --- a/dtlstransportstate.go +++ b/dtlstransportstate.go @@ -7,7 +7,7 @@ package webrtc type DTLSTransportState int const ( - // DTLSTransportStateUnknown is the enum's zero-value + // DTLSTransportStateUnknown is the enum's zero-value. DTLSTransportStateUnknown DTLSTransportState = iota // DTLSTransportStateNew indicates that DTLS has not started negotiating @@ -76,13 +76,14 @@ func (t DTLSTransportState) String() string { } } -// MarshalText implements encoding.TextMarshaler +// MarshalText implements encoding.TextMarshaler. func (t DTLSTransportState) MarshalText() ([]byte, error) { return []byte(t.String()), nil } -// UnmarshalText implements encoding.TextUnmarshaler +// UnmarshalText implements encoding.TextUnmarshaler. func (t *DTLSTransportState) UnmarshalText(b []byte) error { *t = newDTLSTransportState(string(b)) + return nil } diff --git a/errors.go b/errors.go index 8c460d14..908f948c 100644 --- a/errors.go +++ b/errors.go @@ -76,88 +76,100 @@ var ( // and is mutually exclusive. ErrRetransmitsOrPacketLifeTime = errors.New("both MaxPacketLifeTime and MaxRetransmits was set") - // ErrCodecNotFound is returned when a codec search to the Media Engine fails + // ErrCodecNotFound is returned when a codec search to the Media Engine fails. ErrCodecNotFound = errors.New("codec not found") // ErrNoRemoteDescription indicates that an operation was rejected because - // the remote description is not set + // the remote description is not set. ErrNoRemoteDescription = errors.New("remote description is not set") // ErrIncorrectSDPSemantics indicates that the PeerConnection was configured to - // generate SDP Answers with different SDP Semantics than the received Offer + // generate SDP Answers with different SDP Semantics than the received Offer. ErrIncorrectSDPSemantics = errors.New("remote SessionDescription semantics does not match configuration") - // ErrIncorrectSignalingState indicates that the signaling state of PeerConnection is not correct + // ErrIncorrectSignalingState indicates that the signaling state of PeerConnection is not correct. ErrIncorrectSignalingState = errors.New("operation can not be run in current signaling state") // ErrProtocolTooLarge indicates that value given for a DataChannelInit protocol is - // longer then 65535 bytes + // longer then 65535 bytes. ErrProtocolTooLarge = errors.New("protocol is larger then 65535 bytes") // ErrSenderNotCreatedByConnection indicates RemoveTrack was called with a RtpSender not created - // by this PeerConnection + // by this PeerConnection. ErrSenderNotCreatedByConnection = errors.New("RtpSender not created by this PeerConnection") // ErrSessionDescriptionNoFingerprint indicates SetRemoteDescription was called with a SessionDescription that has no - // fingerprint + // fingerprint. ErrSessionDescriptionNoFingerprint = errors.New("SetRemoteDescription called with no fingerprint") // ErrSessionDescriptionInvalidFingerprint indicates SetRemoteDescription was called with a SessionDescription that - // has an invalid fingerprint + // has an invalid fingerprint. ErrSessionDescriptionInvalidFingerprint = errors.New("SetRemoteDescription called with an invalid fingerprint") - // ErrSessionDescriptionConflictingFingerprints indicates SetRemoteDescription was called with a SessionDescription that - // has an conflicting fingerprints - ErrSessionDescriptionConflictingFingerprints = errors.New("SetRemoteDescription called with multiple conflicting fingerprint") + // ErrSessionDescriptionConflictingFingerprints indicates SetRemoteDescription was called with a SessionDescription + // that has an conflicting fingerprints. + ErrSessionDescriptionConflictingFingerprints = errors.New( + "SetRemoteDescription called with multiple conflicting fingerprint", + ) // ErrSessionDescriptionMissingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that - // is missing an ice-ufrag value + // is missing an ice-ufrag value. ErrSessionDescriptionMissingIceUfrag = errors.New("SetRemoteDescription called with no ice-ufrag") // ErrSessionDescriptionMissingIcePwd indicates SetRemoteDescription was called with a SessionDescription that - // is missing an ice-pwd value + // is missing an ice-pwd value. ErrSessionDescriptionMissingIcePwd = errors.New("SetRemoteDescription called with no ice-pwd") - // ErrSessionDescriptionConflictingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that - // contains multiple conflicting ice-ufrag values - ErrSessionDescriptionConflictingIceUfrag = errors.New("SetRemoteDescription called with multiple conflicting ice-ufrag values") + // ErrSessionDescriptionConflictingIceUfrag indicates SetRemoteDescription was called with a SessionDescription + // that contains multiple conflicting ice-ufrag values. + ErrSessionDescriptionConflictingIceUfrag = errors.New( + "SetRemoteDescription called with multiple conflicting ice-ufrag values", + ) - // ErrSessionDescriptionConflictingIcePwd indicates SetRemoteDescription was called with a SessionDescription that - // contains multiple conflicting ice-pwd values - ErrSessionDescriptionConflictingIcePwd = errors.New("SetRemoteDescription called with multiple conflicting ice-pwd values") + // ErrSessionDescriptionConflictingIcePwd indicates SetRemoteDescription was called with a SessionDescription + // that contains multiple conflicting ice-pwd values. + ErrSessionDescriptionConflictingIcePwd = errors.New( + "SetRemoteDescription called with multiple conflicting ice-pwd values", + ) - // ErrNoSRTPProtectionProfile indicates that the DTLS handshake completed and no SRTP Protection Profile was chosen + // ErrNoSRTPProtectionProfile indicates that the DTLS handshake completed and no SRTP Protection Profile was chosen. ErrNoSRTPProtectionProfile = errors.New("DTLS Handshake completed and no SRTP Protection Profile was chosen") - // ErrFailedToGenerateCertificateFingerprint indicates that we failed to generate the fingerprint used for comparing certificates + // ErrFailedToGenerateCertificateFingerprint indicates that we failed to generate the fingerprint + // used for comparing certificates. ErrFailedToGenerateCertificateFingerprint = errors.New("failed to generate certificate fingerprint") - // ErrNoCodecsAvailable indicates that operation isn't possible because the MediaEngine has no codecs available + // ErrNoCodecsAvailable indicates that operation isn't possible because the MediaEngine has no codecs available. ErrNoCodecsAvailable = errors.New("operation failed no codecs are available") - // ErrUnsupportedCodec indicates the remote peer doesn't support the requested codec + // ErrUnsupportedCodec indicates the remote peer doesn't support the requested codec. ErrUnsupportedCodec = errors.New("unable to start track, codec is not supported by remote") - // ErrSenderWithNoCodecs indicates that a RTPSender was created without any codecs. To send media the MediaEngine needs at - // least one configured codec. + // ErrSenderWithNoCodecs indicates that a RTPSender was created without any codecs. To send media the MediaEngine + // needs at least one configured codec. ErrSenderWithNoCodecs = errors.New("unable to populate media section, RTPSender created with no codecs") - // ErrRTPSenderNewTrackHasIncorrectKind indicates that the new track is of a different kind than the previous/original + // ErrRTPSenderNewTrackHasIncorrectKind indicates that the new track is of a different kind than the previous/original. ErrRTPSenderNewTrackHasIncorrectKind = errors.New("new track must be of the same kind as previous") - // ErrRTPSenderNewTrackHasIncorrectEnvelope indicates that the new track has a different envelope than the previous/original + // ErrRTPSenderNewTrackHasIncorrectEnvelope indicates that the new track has a different envelope + // than the previous/original. ErrRTPSenderNewTrackHasIncorrectEnvelope = errors.New("new track must have the same envelope as previous") - // ErrUnbindFailed indicates that a TrackLocal was not able to be unbind + // ErrUnbindFailed indicates that a TrackLocal was not able to be unbind. ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection") - // ErrNoPayloaderForCodec indicates that the requested codec does not have a payloader + // ErrNoPayloaderForCodec indicates that the requested codec does not have a payloader. ErrNoPayloaderForCodec = errors.New("the requested codec does not have a payloader") - // ErrRegisterHeaderExtensionInvalidDirection indicates that a extension was registered with a direction besides `sendonly` or `recvonly` - ErrRegisterHeaderExtensionInvalidDirection = errors.New("a header extension must be registered as 'recvonly', 'sendonly' or both") + // ErrRegisterHeaderExtensionInvalidDirection indicates that a extension was + // registered with a direction besides `sendonly` or `recvonly`. + ErrRegisterHeaderExtensionInvalidDirection = errors.New( + "a header extension must be registered as 'recvonly', 'sendonly' or both", + ) - // ErrSimulcastProbeOverflow indicates that too many Simulcast probe streams are in flight and the requested SSRC was ignored + // ErrSimulcastProbeOverflow indicates that too many Simulcast probe streams are in flight + // and the requested SSRC was ignored. ErrSimulcastProbeOverflow = errors.New("simulcast probe limit has been reached, new SSRC has been discarded") errDetachNotEnabled = errors.New("enable detaching by calling webrtc.DetachDataChannels()") @@ -173,35 +185,49 @@ var ( errICEConnectionNotStarted = errors.New("ICE connection not started") errICECandidateTypeUnknown = errors.New("unknown candidate type") - errICEInvalidConvertCandidateType = errors.New("cannot convert ice.CandidateType into webrtc.ICECandidateType, invalid type") - errICEAgentNotExist = errors.New("ICEAgent does not exist") - errICECandiatesCoversionFailed = errors.New("unable to convert ICE candidates to ICECandidates") - errICERoleUnknown = errors.New("unknown ICE Role") - errICEProtocolUnknown = errors.New("unknown protocol") - errICEGathererNotStarted = errors.New("gatherer not started") + errICEInvalidConvertCandidateType = errors.New( + "cannot convert ice.CandidateType into webrtc.ICECandidateType, invalid type", + ) + errICEAgentNotExist = errors.New("ICEAgent does not exist") + errICECandiatesCoversionFailed = errors.New("unable to convert ICE candidates to ICECandidates") + errICERoleUnknown = errors.New("unknown ICE Role") + errICEProtocolUnknown = errors.New("unknown protocol") + errICEGathererNotStarted = errors.New("gatherer not started") errNetworkTypeUnknown = errors.New("unknown network type") - errSDPDoesNotMatchOffer = errors.New("new sdp does not match previous offer") - errSDPDoesNotMatchAnswer = errors.New("new sdp does not match previous answer") - errPeerConnSDPTypeInvalidValue = errors.New("provided value is not a valid enum value of type SDPType") + errSDPDoesNotMatchOffer = errors.New("new sdp does not match previous offer") + errSDPDoesNotMatchAnswer = errors.New("new sdp does not match previous answer") + errPeerConnSDPTypeInvalidValue = errors.New( + "provided value is not a valid enum value of type SDPType", + ) errPeerConnStateChangeInvalid = errors.New("invalid state change op") errPeerConnStateChangeUnhandled = errors.New("unhandled state change op") errPeerConnSDPTypeInvalidValueSetLocalDescription = errors.New("invalid SDP type supplied to SetLocalDescription()") - errPeerConnRemoteDescriptionWithoutMidValue = errors.New("remoteDescription contained media section without mid value") - errPeerConnRemoteDescriptionNil = errors.New("remoteDescription has not been set yet") - errPeerConnSingleMediaSectionHasExplicitSSRC = errors.New("single media section has an explicit SSRC") - errPeerConnRemoteSSRCAddTransceiver = errors.New("could not add transceiver for remote SSRC") - errPeerConnSimulcastMidRTPExtensionRequired = errors.New("mid RTP Extensions required for Simulcast") - errPeerConnSimulcastStreamIDRTPExtensionRequired = errors.New("stream id RTP Extensions required for Simulcast") - errPeerConnSimulcastIncomingSSRCFailed = errors.New("incoming SSRC failed Simulcast probing") - errPeerConnAddTransceiverFromKindOnlyAcceptsOne = errors.New("AddTransceiverFromKind only accepts one RTPTransceiverInit") - errPeerConnAddTransceiverFromTrackOnlyAcceptsOne = errors.New("AddTransceiverFromTrack only accepts one RTPTransceiverInit") - errPeerConnAddTransceiverFromKindSupport = errors.New("AddTransceiverFromKind currently only supports recvonly") - errPeerConnAddTransceiverFromTrackSupport = errors.New("AddTransceiverFromTrack currently only supports sendonly and sendrecv") - errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider") - errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream") - errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid") + errPeerConnRemoteDescriptionWithoutMidValue = errors.New( + "remoteDescription contained media section without mid value", + ) + errPeerConnRemoteDescriptionNil = errors.New("remoteDescription has not been set yet") + errPeerConnSingleMediaSectionHasExplicitSSRC = errors.New("single media section has an explicit SSRC") + errPeerConnRemoteSSRCAddTransceiver = errors.New("could not add transceiver for remote SSRC") + errPeerConnSimulcastMidRTPExtensionRequired = errors.New("mid RTP Extensions required for Simulcast") + errPeerConnSimulcastStreamIDRTPExtensionRequired = errors.New("stream id RTP Extensions required for Simulcast") + errPeerConnSimulcastIncomingSSRCFailed = errors.New("incoming SSRC failed Simulcast probing") + errPeerConnAddTransceiverFromKindOnlyAcceptsOne = errors.New( + "AddTransceiverFromKind only accepts one RTPTransceiverInit", + ) + errPeerConnAddTransceiverFromTrackOnlyAcceptsOne = errors.New( + "AddTransceiverFromTrack only accepts one RTPTransceiverInit", + ) + errPeerConnAddTransceiverFromKindSupport = errors.New( + "AddTransceiverFromKind currently only supports recvonly", + ) + errPeerConnAddTransceiverFromTrackSupport = errors.New( + "AddTransceiverFromTrack currently only supports sendonly and sendrecv", + ) + errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider") + errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream") + errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid") errRTPReceiverDTLSTransportNil = errors.New("DTLSTransport must not be nil") errRTPReceiverReceiveAlreadyCalled = errors.New("Receive has already been called") @@ -227,14 +253,18 @@ var ( errSDPZeroTransceivers = errors.New("addTransceiverSDP() called with 0 transceivers") errSDPMediaSectionMediaDataChanInvalid = errors.New("invalid Media Section. Media + DataChannel both enabled") - errSDPMediaSectionMultipleTrackInvalid = errors.New("invalid Media Section. Can not have multiple tracks in one MediaSection in UnifiedPlan") + errSDPMediaSectionMultipleTrackInvalid = errors.New( + "invalid Media Section. Can not have multiple tracks in one MediaSection in UnifiedPlan", + ) errSettingEngineSetAnsweringDTLSRole = errors.New("SetAnsweringDTLSRole must DTLSRoleClient or DTLSRoleServer") errSignalingStateCannotRollback = errors.New("can't rollback from stable state") errSignalingStateProposedTransitionInvalid = errors.New("invalid proposed signaling state transition") - errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state") + errStatsICECandidateStateInvalid = errors.New( + "cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state", + ) errInvalidICECredentialTypeString = errors.New("invalid ICECredentialType") errInvalidICEServer = errors.New("invalid ICEServer") diff --git a/examples/bandwidth-estimation-from-disk/main.go b/examples/bandwidth-estimation-from-disk/main.go index 39aa35bc..817b7174 100644 --- a/examples/bandwidth-estimation-from-disk/main.go +++ b/examples/bandwidth-estimation-from-disk/main.go @@ -39,8 +39,7 @@ const ( ivfHeaderSize = 32 ) -// nolint: gocognit -func main() { +func main() { //nolint:gocognit,cyclop,maintidx qualityLevels := []struct { fileName string bitrate int @@ -58,9 +57,9 @@ func main() { } } - i := &interceptor.Registry{} - m := &webrtc.MediaEngine{} - if err := m.RegisterDefaultCodecs(); err != nil { + interceptorRegistry := &interceptor.Registry{} + mediaEngine := &webrtc.MediaEngine{} + if err := mediaEngine.RegisterDefaultCodecs(); err != nil { panic(err) } @@ -81,17 +80,19 @@ func main() { estimatorChan <- estimator }) - i.Add(congestionController) - if err = webrtc.ConfigureTWCCHeaderExtensionSender(m, i); err != nil { + interceptorRegistry.Add(congestionController) + if err = webrtc.ConfigureTWCCHeaderExtensionSender(mediaEngine, interceptorRegistry); err != nil { panic(err) } - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create a new RTCPeerConnection - peerConnection, err := webrtc.NewAPI(webrtc.WithInterceptorRegistry(i), webrtc.WithMediaEngine(m)).NewPeerConnection(webrtc.Configuration{ + peerConnection, err := webrtc.NewAPI( + webrtc.WithInterceptorRegistry(interceptorRegistry), webrtc.WithMediaEngine(mediaEngine), + ).NewPeerConnection(webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { URLs: []string{"stun:stun.l.google.com:19302"}, @@ -111,7 +112,9 @@ func main() { estimator := <-estimatorChan // Create a video track - videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") + videoTrack, err := webrtc.NewTrackLocalStaticSample( + webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion", + ) if err != nil { panic(err) } @@ -141,8 +144,8 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) }) // Wait for the offer to be pasted @@ -193,14 +196,21 @@ func main() { // It is important to use a time.Ticker instead of time.Sleep because // * avoids accumulating skew, just calling time.Sleep didn't compensate for the time spent parsing the data // * works around latency issues with Sleep (see https://github.com/golang/go/issues/44343) - ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)) + ticker := time.NewTicker( + time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000), + ) defer ticker.Stop() frame := []byte{} frameHeader := &ivfreader.IVFFrameHeader{} currentTimestamp := uint64(0) switchQualityLevel := func(newQualityLevel int) { - fmt.Printf("Switching from %s to %s \n", qualityLevels[currentQuality].fileName, qualityLevels[newQualityLevel].fileName) + fmt.Printf( + "Switching from %s to %s \n", + qualityLevels[currentQuality].fileName, + qualityLevels[newQualityLevel].fileName, + ) + currentQuality = newQualityLevel ivf.ResetReader(setReaderFile(qualityLevels[currentQuality].fileName)) for { @@ -255,11 +265,12 @@ func setReaderFile(filename string) func(_ int64) io.Reader { if _, err = file.Seek(ivfHeaderSize, io.SeekStart); err != nil { panic(err) } + return file } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -276,10 +287,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -289,7 +301,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/broadcast/main.go b/examples/broadcast/main.go index 47eb7f2d..016bf492 100644 --- a/examples/broadcast/main.go +++ b/examples/broadcast/main.go @@ -22,7 +22,8 @@ import ( "github.com/pion/webrtc/v4" ) -func main() { // nolint:gocognit +// nolint:gocognit, cyclop +func main() { port := flag.Int("port", 8080, "http server port") flag.Parse() @@ -41,8 +42,8 @@ func main() { // nolint:gocognit }, } - m := &webrtc.MediaEngine{} - if err := m.RegisterDefaultCodecs(); err != nil { + mediaEngine := &webrtc.MediaEngine{} + if err := mediaEngine.RegisterDefaultCodecs(); err != nil { panic(err) } @@ -50,10 +51,10 @@ func main() { // nolint:gocognit // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Use the default set of Interceptors - if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err := webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } @@ -65,10 +66,13 @@ func main() { // nolint:gocognit if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Create a new RTCPeerConnection - peerConnection, err := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)).NewPeerConnection(peerConnectionConfig) + peerConnection, err := webrtc.NewAPI( + webrtc.WithMediaEngine(mediaEngine), + webrtc.WithInterceptorRegistry(interceptorRegistry), + ).NewPeerConnection(peerConnectionConfig) if err != nil { panic(err) } @@ -199,7 +203,7 @@ func main() { // nolint:gocognit } } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -209,7 +213,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { @@ -221,12 +225,12 @@ func decode(in string, obj *webrtc.SessionDescription) { } } -// httpSDPServer starts a HTTP Server that consumes SDPs +// httpSDPServer starts a HTTP Server that consumes SDPs. func httpSDPServer(port int) chan string { sdpChan := make(chan string) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") //nolint: errcheck + http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { + body, _ := io.ReadAll(req.Body) + fmt.Fprintf(res, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/custom-logger/main.go b/examples/custom-logger/main.go index dc733483..f634289d 100644 --- a/examples/custom-logger/main.go +++ b/examples/custom-logger/main.go @@ -22,7 +22,7 @@ import ( // behavior per subsystem (ICE, DTLS, SCTP...) type customLogger struct{} -// Print all messages except trace +// Print all messages except trace. func (c customLogger) Trace(string) {} func (c customLogger) Tracef(string, ...interface{}) {} @@ -45,14 +45,16 @@ func (c customLogger) Errorf(format string, args ...interface{}) { // customLoggerFactory satisfies the interface logging.LoggerFactory // This allows us to create different loggers per subsystem. So we can -// add custom behavior +// add custom behavior. type customLoggerFactory struct{} func (c customLoggerFactory) NewLogger(subsystem string) logging.LeveledLogger { fmt.Printf("Creating logger for %s \n", subsystem) + return customLogger{} } +// nolint: cyclop func main() { // Create a new API with a custom logger // This SettingEngine allows non-standard WebRTC behavior @@ -90,18 +92,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - offerPeerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (offerer)\n", s.String()) + offerPeerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (offerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -110,11 +113,12 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - answerPeerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String()) + answerPeerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (answerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") @@ -124,9 +128,9 @@ func main() { // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - answerPeerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - if iceErr := offerPeerConnection.AddICECandidate(i.ToJSON()); iceErr != nil { + answerPeerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + if iceErr := offerPeerConnection.AddICECandidate(candidate.ToJSON()); iceErr != nil { panic(iceErr) } } @@ -134,9 +138,9 @@ func main() { // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - offerPeerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - if iceErr := answerPeerConnection.AddICECandidate(i.ToJSON()); iceErr != nil { + offerPeerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + if iceErr := answerPeerConnection.AddICECandidate(candidate.ToJSON()); iceErr != nil { panic(iceErr) } } diff --git a/examples/data-channels-detach/main.go b/examples/data-channels-detach/main.go index b7d8af52..d242fb88 100644 --- a/examples/data-channels-detach/main.go +++ b/examples/data-channels-detach/main.go @@ -1,7 +1,10 @@ // SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT -// data-channels-detach is an example that shows how you can detach a data channel. This allows direct access the underlying [pion/datachannel](https://github.com/pion/datachannel). This allows you to interact with the data channel using a more idiomatic API based on the `io.ReadWriteCloser` interface. +// data-channels-detach is an example that shows how you can detach a data channel. +// This allows direct access the underlying [pion/datachannel](https://github.com/pion/datachannel). +// This allows you to interact with the data channel using a more idiomatic API based on +// the `io.ReadWriteCloser` interface. package main import ( @@ -57,18 +60,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -76,15 +80,15 @@ func main() { }) // Register data channel creation handling - peerConnection.OnDataChannel(func(d *webrtc.DataChannel) { - fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID()) + peerConnection.OnDataChannel(func(dataChannel *webrtc.DataChannel) { + fmt.Printf("New DataChannel %s %d\n", dataChannel.Label(), dataChannel.ID()) // Register channel opening handling - d.OnOpen(func() { - fmt.Printf("Data channel '%s'-'%d' open.\n", d.Label(), d.ID()) + dataChannel.OnOpen(func() { + fmt.Printf("Data channel '%s'-'%d' open.\n", dataChannel.Label(), dataChannel.ID()) // Detach the data channel - raw, dErr := d.Detach() + raw, dErr := dataChannel.Detach() if dErr != nil { panic(dErr) } @@ -134,13 +138,14 @@ func main() { select {} } -// ReadLoop shows how to read from the datachannel directly +// ReadLoop shows how to read from the datachannel directly. func ReadLoop(d io.Reader) { for { buffer := make([]byte, messageSize) n, err := d.Read(buffer) if err != nil { fmt.Println("Datachannel closed; Exit the readloop:", err) + return } @@ -148,12 +153,14 @@ func ReadLoop(d io.Reader) { } } -// WriteLoop shows how to write to the datachannel directly +// WriteLoop shows how to write to the datachannel directly. func WriteLoop(d io.Writer) { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for range ticker.C { - message, err := randutil.GenerateCryptoRandomString(messageSize, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + message, err := randutil.GenerateCryptoRandomString( + messageSize, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + ) if err != nil { panic(err) } @@ -165,7 +172,7 @@ func WriteLoop(d io.Writer) { } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -182,10 +189,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -195,7 +203,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/data-channels-flow-control/main.go b/examples/data-channels-flow-control/main.go index eed504dd..38d8ba93 100644 --- a/examples/data-channels-flow-control/main.go +++ b/examples/data-channels-flow-control/main.go @@ -59,18 +59,21 @@ func createOfferer() *webrtc.PeerConnection { sendMoreCh := make(chan struct{}, 1) // Create a datachannel with label 'data' - dc, err := pc.CreateDataChannel("data", options) + dataChannel, err := pc.CreateDataChannel("data", options) check(err) // Register channel opening handling - dc.OnOpen(func() { - log.Printf("OnOpen: %s-%d. Start sending a series of 1024-byte packets as fast as it can\n", dc.Label(), dc.ID()) + dataChannel.OnOpen(func() { + log.Printf( + "OnOpen: %s-%d. Start sending a series of 1024-byte packets as fast as it can\n", + dataChannel.Label(), dataChannel.ID(), + ) for { - err2 := dc.Send(buf) + err2 := dataChannel.Send(buf) check(err2) - if dc.BufferedAmount() > maxBufferedAmount { + if dataChannel.BufferedAmount() > maxBufferedAmount { // Wait until the bufferedAmount becomes lower than the threshold <-sendMoreCh } @@ -79,10 +82,10 @@ func createOfferer() *webrtc.PeerConnection { // Set bufferedAmountLowThreshold so that we can get notified when // we can send more - dc.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold) + dataChannel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold) // This callback is made when the current bufferedAmount becomes lower than the threshold - dc.OnBufferedAmountLow(func() { + dataChannel.OnBufferedAmountLow(func() { // Make sure to not block this channel or perform long running operations in this callback // This callback is executed by pion/sctp. If this callback is blocking it will stop operations select { @@ -104,12 +107,12 @@ func createAnswerer() *webrtc.PeerConnection { pc, err := webrtc.NewPeerConnection(config) check(err) - pc.OnDataChannel(func(dc *webrtc.DataChannel) { + pc.OnDataChannel(func(dataChannel *webrtc.DataChannel) { var totalBytesReceived uint64 // Register channel opening handling - dc.OnOpen(func() { - log.Printf("OnOpen: %s-%d. Start receiving data", dc.Label(), dc.ID()) + dataChannel.OnOpen(func() { + log.Printf("OnOpen: %s-%d. Start receiving data", dataChannel.Label(), dataChannel.ID()) since := time.Now() // Start printing out the observed throughput @@ -122,7 +125,7 @@ func createAnswerer() *webrtc.PeerConnection { }) // Register the OnMessage to handle incoming messages - dc.OnMessage(func(dcMsg webrtc.DataChannelMessage) { + dataChannel.OnMessage(func(dcMsg webrtc.DataChannelMessage) { n := len(dcMsg.Data) atomic.AddUint64(&totalBytesReceived, uint64(n)) }) @@ -148,34 +151,35 @@ func main() { // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - answerPC.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - check(offerPC.AddICECandidate(i.ToJSON())) + answerPC.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + check(offerPC.AddICECandidate(candidate.ToJSON())) } }) // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - offerPC.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - check(answerPC.AddICECandidate(i.ToJSON())) + offerPC.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + check(answerPC.AddICECandidate(candidate.ToJSON())) } }) // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - offerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (offerer)\n", s.String()) + offerPC.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (offerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -184,18 +188,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - answerPC.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String()) + answerPC.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (answerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) diff --git a/examples/data-channels/main.go b/examples/data-channels/main.go index 75bf245e..6fdc2a86 100644 --- a/examples/data-channels/main.go +++ b/examples/data-channels/main.go @@ -19,6 +19,7 @@ import ( "github.com/pion/webrtc/v4" ) +// nolint:cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. @@ -44,18 +45,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -63,12 +65,15 @@ func main() { }) // Register data channel creation handling - peerConnection.OnDataChannel(func(d *webrtc.DataChannel) { - fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID()) + peerConnection.OnDataChannel(func(dataChannel *webrtc.DataChannel) { + fmt.Printf("New DataChannel %s %d\n", dataChannel.Label(), dataChannel.ID()) // Register channel opening handling - d.OnOpen(func() { - fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label(), d.ID()) + dataChannel.OnOpen(func() { + fmt.Printf( + "Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", + dataChannel.Label(), dataChannel.ID(), + ) ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -80,15 +85,15 @@ func main() { // Send the message as text fmt.Printf("Sending '%s'\n", message) - if sendErr = d.SendText(message); sendErr != nil { + if sendErr = dataChannel.SendText(message); sendErr != nil { panic(sendErr) } } }) // Register text message handling - d.OnMessage(func(msg webrtc.DataChannelMessage) { - fmt.Printf("Message from DataChannel '%s': '%s'\n", d.Label(), string(msg.Data)) + dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { + fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data)) }) }) @@ -129,7 +134,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -146,10 +151,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -159,7 +165,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/examples.go b/examples/examples.go index dbcd773b..4fec36e5 100644 --- a/examples/examples.go +++ b/examples/examples.go @@ -49,10 +49,11 @@ func serve(addr string) error { // Serve the required pages // DIY 'mux' to avoid additional dependencies - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - url := r.URL.Path + http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { + url := req.URL.Path if url == "/wasm_exec.js" { - http.FileServer(http.Dir(filepath.Join(build.Default.GOROOT, "misc/wasm/"))).ServeHTTP(w, r) + http.FileServer(http.Dir(filepath.Join(build.Default.GOROOT, "misc/wasm/"))).ServeHTTP(res, req) + return } @@ -73,7 +74,11 @@ func serve(addr string) error { } fiddle := filepath.Join(exampleLink, "jsfiddle") if len(parts[4]) != 0 { - http.StripPrefix("/example/"+exampleType+"/"+exampleLink+"/", http.FileServer(http.Dir(fiddle))).ServeHTTP(w, r) + http.StripPrefix( + "/example/"+exampleType+"/"+exampleLink+"/", + http.FileServer(http.Dir(fiddle)), + ).ServeHTTP(res, req) + return } @@ -91,16 +96,17 @@ func serve(addr string) error { exampleType == "js", } - err = temp.Execute(w, data) + err = temp.Execute(res, data) if err != nil { panic(err) } + return } } // Serve the main page - err := homeTemplate.Execute(w, examples) + err := homeTemplate.Execute(res, examples) if err != nil { panic(err) } diff --git a/examples/ice-restart/main.go b/examples/ice-restart/main.go index 4af4093e..dd9b8738 100644 --- a/examples/ice-restart/main.go +++ b/examples/ice-restart/main.go @@ -15,7 +15,8 @@ import ( var peerConnection *webrtc.PeerConnection //nolint -func doSignaling(w http.ResponseWriter, r *http.Request) { +// nolint: cyclop +func doSignaling(res http.ResponseWriter, req *http.Request) { var err error if peerConnection == nil { @@ -42,7 +43,7 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { } var offer webrtc.SessionDescription - if err = json.NewDecoder(r.Body).Decode(&offer); err != nil { + if err = json.NewDecoder(req.Body).Decode(&offer); err != nil { panic(err) } @@ -70,8 +71,8 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { panic(err) } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(response); err != nil { + res.Header().Set("Content-Type", "application/json") + if _, err := res.Write(response); err != nil { panic(err) } } diff --git a/examples/ice-single-port/main.go b/examples/ice-single-port/main.go index f7dc2f7d..35aa0627 100644 --- a/examples/ice-single-port/main.go +++ b/examples/ice-single-port/main.go @@ -20,7 +20,7 @@ import ( var api *webrtc.API //nolint // Everything below is the Pion WebRTC API! Thanks for using it ❤️. -func doSignaling(w http.ResponseWriter, r *http.Request) { +func doSignaling(res http.ResponseWriter, req *http.Request) { peerConnection, err := api.NewPeerConnection(webrtc.Configuration{}) if err != nil { panic(err) @@ -44,7 +44,7 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { }) var offer webrtc.SessionDescription - if err = json.NewDecoder(r.Body).Decode(&offer); err != nil { + if err = json.NewDecoder(req.Body).Decode(&offer); err != nil { panic(err) } @@ -72,8 +72,8 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { panic(err) } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(response); err != nil { + res.Header().Set("Content-Type", "application/json") + if _, err := res.Write(response); err != nil { panic(err) } } diff --git a/examples/ice-tcp/main.go b/examples/ice-tcp/main.go index d17df5af..4ff4e47a 100644 --- a/examples/ice-tcp/main.go +++ b/examples/ice-tcp/main.go @@ -21,7 +21,7 @@ import ( var api *webrtc.API //nolint -func doSignaling(w http.ResponseWriter, r *http.Request) { +func doSignaling(res http.ResponseWriter, req *http.Request) { //nolint:cyclop peerConnection, err := api.NewPeerConnection(webrtc.Configuration{}) if err != nil { panic(err) @@ -48,7 +48,7 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { }) var offer webrtc.SessionDescription - if err = json.NewDecoder(r.Body).Decode(&offer); err != nil { + if err = json.NewDecoder(req.Body).Decode(&offer); err != nil { panic(err) } @@ -76,12 +76,13 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { panic(err) } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(response); err != nil { + res.Header().Set("Content-Type", "application/json") + if _, err := res.Write(response); err != nil { panic(err) } } +//nolint:cyclop func main() { settingEngine := webrtc.SettingEngine{} diff --git a/examples/insertable-streams/main.go b/examples/insertable-streams/main.go index 9a8188b7..634cd195 100644 --- a/examples/insertable-streams/main.go +++ b/examples/insertable-streams/main.go @@ -26,7 +26,7 @@ import ( const cipherKey = 0xAA -// nolint:gocognit +// nolint:gocognit, cyclop func main() { peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ @@ -45,7 +45,9 @@ func main() { }() // Create a video track - videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") + videoTrack, err := webrtc.NewTrackLocalStaticSample( + webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion", + ) if err != nil { panic(err) } @@ -88,7 +90,9 @@ func main() { // It is important to use a time.Ticker instead of time.Sleep because // * avoids accumulating skew, just calling time.Sleep didn't compensate for the time spent parsing the data // * works around latency issues with Sleep (see https://github.com/golang/go/issues/44343) - ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)) + ticker := time.NewTicker( + time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000), + ) defer ticker.Stop() for range ticker.C { frame, _, ivfErr := ivf.ParseNextFrame() @@ -123,18 +127,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -176,7 +181,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -193,10 +198,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -206,7 +212,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/ortc-media/main.go b/examples/ortc-media/main.go index 318ccbde..a2456d29 100644 --- a/examples/ortc-media/main.go +++ b/examples/ortc-media/main.go @@ -30,6 +30,7 @@ const ( videoFileName = "output.ivf" ) +// nolint:cyclop func main() { isOffer := flag.Bool("offer", false, "Act as the offerer if set") port := flag.Int("port", 8080, "http server port") @@ -45,13 +46,13 @@ func main() { } // Use default Codecs - m := &webrtc.MediaEngine{} - if err := m.RegisterDefaultCodecs(); err != nil { + mediaEngine := &webrtc.MediaEngine{} + if err := mediaEngine.RegisterDefaultCodecs(); err != nil { panic(err) } // Create an API object - api := webrtc.NewAPI(webrtc.WithMediaEngine(m)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine)) // Create the ICE gatherer gatherer, err := api.NewICEGatherer(iceOptions) @@ -74,7 +75,7 @@ func main() { rtpSendParameters webrtc.RTPSendParameters ) - if *isOffer { + if *isOffer { //nolint:nestif // Open the video file file, fileErr := os.Open(videoFileName) if fileErr != nil { @@ -109,8 +110,8 @@ func main() { } gatherFinished := make(chan struct{}) - gatherer.OnLocalCandidate(func(i *webrtc.ICECandidate) { - if i == nil { + gatherer.OnLocalCandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { close(gatherFinished) } }) @@ -137,7 +138,7 @@ func main() { panic(err) } - s := Signal{ + signal := Signal{ ICECandidates: iceCandidates, ICEParameters: iceParams, DTLSParameters: dtlsParams, @@ -147,7 +148,7 @@ func main() { iceRole := webrtc.ICERoleControlled // Exchange the information - fmt.Println(encode(&s)) + fmt.Println(encode(&signal)) remoteSignal := Signal{} if *isOffer { @@ -196,7 +197,7 @@ func main() { select {} } -// Given a FourCC value return a Track +// Given a FourCC value return a Track. func fourCCToTrack(fourCC string) *webrtc.TrackLocalStaticSample { // Determine video codec var trackCodec string @@ -220,9 +221,11 @@ func fourCCToTrack(fourCC string) *webrtc.TrackLocalStaticSample { return trackLocal } -// Write a file to Track +// Write a file to Track. func writeFileToTrack(ivf *ivfreader.IVFReader, header *ivfreader.IVFFileHeader, track *webrtc.TrackLocalStaticSample) { - ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)) + ticker := time.NewTicker( + time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000), + ) defer ticker.Stop() for ; true; <-ticker.C { frame, _, err := ivf.ParseNextFrame() @@ -251,7 +254,7 @@ type Signal struct { RTPSendParameters webrtc.RTPSendParameters `json:"rtpSendParameters"` } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -268,10 +271,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *Signal) string { b, err := json.Marshal(obj) if err != nil { @@ -281,7 +285,7 @@ func encode(obj *Signal) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *Signal) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { @@ -293,12 +297,12 @@ func decode(in string, obj *Signal) { } } -// httpSDPServer starts a HTTP Server that consumes SDPs +// httpSDPServer starts a HTTP Server that consumes SDPs. func httpSDPServer(port int) chan string { sdpChan := make(chan string) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") //nolint: errcheck + http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { + body, _ := io.ReadAll(req.Body) + fmt.Fprintf(res, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/ortc/main.go b/examples/ortc/main.go index 43d501d8..6997fdd0 100644 --- a/examples/ortc/main.go +++ b/examples/ortc/main.go @@ -25,6 +25,7 @@ import ( "github.com/pion/webrtc/v4" ) +// nolint:cyclop func main() { isOffer := flag.Bool("offer", false, "Act as the offerer if set") port := flag.Int("port", 8080, "http server port") @@ -72,8 +73,8 @@ func main() { }) gatherFinished := make(chan struct{}) - gatherer.OnLocalCandidate(func(i *webrtc.ICECandidate) { - if i == nil { + gatherer.OnLocalCandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { close(gatherFinished) } }) @@ -181,7 +182,10 @@ type Signal struct { func handleOnOpen(channel *webrtc.DataChannel) func() { return func() { - fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", channel.Label(), channel.ID()) + fmt.Printf( + "Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", + channel.Label(), channel.ID(), + ) ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() @@ -199,7 +203,7 @@ func handleOnOpen(channel *webrtc.DataChannel) func() { } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -216,10 +220,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj Signal) string { b, err := json.Marshal(obj) if err != nil { @@ -229,7 +234,7 @@ func encode(obj Signal) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *Signal) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { @@ -241,12 +246,12 @@ func decode(in string, obj *Signal) { } } -// httpSDPServer starts a HTTP Server that consumes SDPs +// httpSDPServer starts a HTTP Server that consumes SDPs. func httpSDPServer(port int) chan string { sdpChan := make(chan string) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - body, _ := io.ReadAll(r.Body) - fmt.Fprintf(w, "done") //nolint: errcheck + http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { + body, _ := io.ReadAll(req.Body) + fmt.Fprintf(res, "done") //nolint: errcheck sdpChan <- string(body) }) diff --git a/examples/pion-to-pion/answer/main.go b/examples/pion-to-pion/answer/main.go index e74427a4..ddd4e2dd 100644 --- a/examples/pion-to-pion/answer/main.go +++ b/examples/pion-to-pion/answer/main.go @@ -19,8 +19,8 @@ import ( "github.com/pion/webrtc/v4" ) -func signalCandidate(addr string, c *webrtc.ICECandidate) error { - payload := []byte(c.ToJSON().Candidate) +func signalCandidate(addr string, candidate *webrtc.ICECandidate) error { + payload := []byte(candidate.ToJSON().Candidate) resp, err := http.Post(fmt.Sprintf("http://%s/candidate", addr), // nolint:noctx "application/json; charset=utf-8", bytes.NewReader(payload)) if err != nil { @@ -30,7 +30,8 @@ func signalCandidate(addr string, c *webrtc.ICECandidate) error { return resp.Body.Close() } -func main() { // nolint:gocognit +// nolint:gocognit, cyclop +func main() { offerAddr := flag.String("offer-address", "localhost:50000", "Address that the Offer HTTP server is hosted on.") answerAddr := flag.String("answer-address", ":60000", "Address that the Answer HTTP server is hosted on.") flag.Parse() @@ -61,8 +62,8 @@ func main() { // nolint:gocognit // When an ICE candidate is available send to the other Pion instance // the other Pion instance will add this candidate by calling AddICECandidate - peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) { - if c == nil { + peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { return } @@ -71,8 +72,8 @@ func main() { // nolint:gocognit desc := peerConnection.RemoteDescription() if desc == nil { - pendingCandidates = append(pendingCandidates, c) - } else if onICECandidateErr := signalCandidate(*offerAddr, c); onICECandidateErr != nil { + pendingCandidates = append(pendingCandidates, candidate) + } else if onICECandidateErr := signalCandidate(*offerAddr, candidate); onICECandidateErr != nil { panic(onICECandidateErr) } }) @@ -80,20 +81,22 @@ func main() { // nolint:gocognit // A HTTP handler that allows the other Pion instance to send us ICE candidates // This allows us to add ICE candidates faster, we don't have to wait for STUN or TURN // candidates which may be slower - http.HandleFunc("/candidate", func(w http.ResponseWriter, r *http.Request) { //nolint: revive - candidate, candidateErr := io.ReadAll(r.Body) + http.HandleFunc("/candidate", func(res http.ResponseWriter, req *http.Request) { //nolint: revive + candidate, candidateErr := io.ReadAll(req.Body) if candidateErr != nil { panic(candidateErr) } - if candidateErr := peerConnection.AddICECandidate(webrtc.ICECandidateInit{Candidate: string(candidate)}); candidateErr != nil { + if candidateErr := peerConnection.AddICECandidate( + webrtc.ICECandidateInit{Candidate: string(candidate)}, + ); candidateErr != nil { panic(candidateErr) } }) // A HTTP handler that processes a SessionDescription given to us from the other Pion process - http.HandleFunc("/sdp", func(w http.ResponseWriter, r *http.Request) { // nolint: revive + http.HandleFunc("/sdp", func(res http.ResponseWriter, req *http.Request) { // nolint: revive sdp := webrtc.SessionDescription{} - if err := json.NewDecoder(r.Body).Decode(&sdp); err != nil { + if err := json.NewDecoder(req.Body).Decode(&sdp); err != nil { panic(err) } @@ -112,7 +115,11 @@ func main() { // nolint:gocognit if err != nil { panic(err) } - resp, err := http.Post(fmt.Sprintf("http://%s/sdp", *offerAddr), "application/json; charset=utf-8", bytes.NewReader(payload)) // nolint:noctx + resp, err := http.Post( //nolint:noctx + fmt.Sprintf("http://%s/sdp", *offerAddr), + "application/json; charset=utf-8", + bytes.NewReader(payload), + ) // nolint:noctx if err != nil { panic(err) } else if closeErr := resp.Body.Close(); closeErr != nil { @@ -137,18 +144,19 @@ func main() { // nolint:gocognit // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -156,32 +164,37 @@ func main() { // nolint:gocognit }) // Register data channel creation handling - peerConnection.OnDataChannel(func(d *webrtc.DataChannel) { - fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID()) + peerConnection.OnDataChannel(func(dataChannel *webrtc.DataChannel) { + fmt.Printf("New DataChannel %s %d\n", dataChannel.Label(), dataChannel.ID()) // Register channel opening handling - d.OnOpen(func() { - fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", d.Label(), d.ID()) + dataChannel.OnOpen(func() { + fmt.Printf( + "Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", + dataChannel.Label(), dataChannel.ID(), + ) ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for range ticker.C { - message, sendTextErr := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + message, sendTextErr := randutil.GenerateCryptoRandomString( + 15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + ) if sendTextErr != nil { panic(sendTextErr) } // Send the message as text fmt.Printf("Sending '%s'\n", message) - if sendTextErr = d.SendText(message); sendTextErr != nil { + if sendTextErr = dataChannel.SendText(message); sendTextErr != nil { panic(sendTextErr) } } }) // Register text message handling - d.OnMessage(func(msg webrtc.DataChannelMessage) { - fmt.Printf("Message from DataChannel '%s': '%s'\n", d.Label(), string(msg.Data)) + dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { + fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data)) }) }) diff --git a/examples/pion-to-pion/offer/main.go b/examples/pion-to-pion/offer/main.go index a299e5e9..f7637595 100644 --- a/examples/pion-to-pion/offer/main.go +++ b/examples/pion-to-pion/offer/main.go @@ -19,9 +19,13 @@ import ( "github.com/pion/webrtc/v4" ) -func signalCandidate(addr string, c *webrtc.ICECandidate) error { - payload := []byte(c.ToJSON().Candidate) - resp, err := http.Post(fmt.Sprintf("http://%s/candidate", addr), "application/json; charset=utf-8", bytes.NewReader(payload)) //nolint:noctx +func signalCandidate(addr string, candidate *webrtc.ICECandidate) error { + payload := []byte(candidate.ToJSON().Candidate) + resp, err := http.Post( //nolint:noctx + fmt.Sprintf("http://%s/candidate", addr), + "application/json; charset=utf-8", + bytes.NewReader(payload), + ) if err != nil { return err } @@ -29,7 +33,8 @@ func signalCandidate(addr string, c *webrtc.ICECandidate) error { return resp.Body.Close() } -func main() { //nolint:gocognit +//nolint:gocognit, cyclop +func main() { offerAddr := flag.String("offer-address", ":50000", "Address that the Offer HTTP server is hosted on.") answerAddr := flag.String("answer-address", "127.0.0.1:60000", "Address that the Answer HTTP server is hosted on.") flag.Parse() @@ -61,8 +66,8 @@ func main() { //nolint:gocognit // When an ICE candidate is available send to the other Pion instance // the other Pion instance will add this candidate by calling AddICECandidate - peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) { - if c == nil { + peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { return } @@ -71,8 +76,8 @@ func main() { //nolint:gocognit desc := peerConnection.RemoteDescription() if desc == nil { - pendingCandidates = append(pendingCandidates, c) - } else if onICECandidateErr := signalCandidate(*answerAddr, c); onICECandidateErr != nil { + pendingCandidates = append(pendingCandidates, candidate) + } else if onICECandidateErr := signalCandidate(*answerAddr, candidate); onICECandidateErr != nil { panic(onICECandidateErr) } }) @@ -80,20 +85,22 @@ func main() { //nolint:gocognit // A HTTP handler that allows the other Pion instance to send us ICE candidates // This allows us to add ICE candidates faster, we don't have to wait for STUN or TURN // candidates which may be slower - http.HandleFunc("/candidate", func(w http.ResponseWriter, r *http.Request) { //nolint: revive - candidate, candidateErr := io.ReadAll(r.Body) + http.HandleFunc("/candidate", func(res http.ResponseWriter, req *http.Request) { //nolint: revive + candidate, candidateErr := io.ReadAll(req.Body) if candidateErr != nil { panic(candidateErr) } - if candidateErr := peerConnection.AddICECandidate(webrtc.ICECandidateInit{Candidate: string(candidate)}); candidateErr != nil { + if candidateErr := peerConnection.AddICECandidate( + webrtc.ICECandidateInit{Candidate: string(candidate)}, + ); candidateErr != nil { panic(candidateErr) } }) // A HTTP handler that processes a SessionDescription given to us from the other Pion process - http.HandleFunc("/sdp", func(w http.ResponseWriter, r *http.Request) { //nolint: revive + http.HandleFunc("/sdp", func(res http.ResponseWriter, req *http.Request) { //nolint: revive sdp := webrtc.SessionDescription{} - if sdpErr := json.NewDecoder(r.Body).Decode(&sdp); sdpErr != nil { + if sdpErr := json.NewDecoder(req.Body).Decode(&sdp); sdpErr != nil { panic(sdpErr) } @@ -122,18 +129,19 @@ func main() { //nolint:gocognit // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -142,12 +150,17 @@ func main() { //nolint:gocognit // Register channel opening handling dataChannel.OnOpen(func() { - fmt.Printf("Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", dataChannel.Label(), dataChannel.ID()) + fmt.Printf( + "Data channel '%s'-'%d' open. Random messages will now be sent to any connected DataChannels every 5 seconds\n", + dataChannel.Label(), dataChannel.ID(), + ) ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for range ticker.C { - message, sendTextErr := randutil.GenerateCryptoRandomString(15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + message, sendTextErr := randutil.GenerateCryptoRandomString( + 15, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + ) if sendTextErr != nil { panic(sendTextErr) } @@ -182,7 +195,11 @@ func main() { //nolint:gocognit if err != nil { panic(err) } - resp, err := http.Post(fmt.Sprintf("http://%s/sdp", *answerAddr), "application/json; charset=utf-8", bytes.NewReader(payload)) // nolint:noctx + resp, err := http.Post( //nolint:noctx + fmt.Sprintf("http://%s/sdp", *answerAddr), + "application/json; charset=utf-8", + bytes.NewReader(payload), + ) if err != nil { panic(err) } else if err := resp.Body.Close(); err != nil { diff --git a/examples/play-from-disk-renegotiation/main.go b/examples/play-from-disk-renegotiation/main.go index 7e871f76..cc0ed3b0 100644 --- a/examples/play-from-disk-renegotiation/main.go +++ b/examples/play-from-disk-renegotiation/main.go @@ -24,10 +24,10 @@ import ( var peerConnection *webrtc.PeerConnection //nolint // doSignaling exchanges all state of the local PeerConnection and is called -// every time a video is added or removed -func doSignaling(w http.ResponseWriter, r *http.Request) { +// every time a video is added or removed. +func doSignaling(res http.ResponseWriter, req *http.Request) { var offer webrtc.SessionDescription - if err := json.NewDecoder(r.Body).Decode(&offer); err != nil { + if err := json.NewDecoder(req.Body).Decode(&offer); err != nil { panic(err) } @@ -55,24 +55,24 @@ func doSignaling(w http.ResponseWriter, r *http.Request) { panic(err) } - w.Header().Set("Content-Type", "application/json") - if _, err := w.Write(response); err != nil { + res.Header().Set("Content-Type", "application/json") + if _, err := res.Write(response); err != nil { panic(err) } } -// Add a single video track -func createPeerConnection(w http.ResponseWriter, r *http.Request) { +// Add a single video track. +func createPeerConnection(res http.ResponseWriter, req *http.Request) { if peerConnection.ConnectionState() != webrtc.PeerConnectionStateNew { panic(fmt.Sprintf("createPeerConnection called in non-new state (%s)", peerConnection.ConnectionState())) } - doSignaling(w, r) + doSignaling(res, req) fmt.Println("PeerConnection has been created") } -// Add a single video track -func addVideo(w http.ResponseWriter, r *http.Request) { +// Add a single video track. +func addVideo(res http.ResponseWriter, req *http.Request) { videoTrack, err := webrtc.NewTrackLocalStaticSample( webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()), @@ -99,19 +99,19 @@ func addVideo(w http.ResponseWriter, r *http.Request) { }() go writeVideoToTrack(videoTrack) - doSignaling(w, r) + doSignaling(res, req) fmt.Println("Video track has been added") } -// Remove a single sender -func removeVideo(w http.ResponseWriter, r *http.Request) { +// Remove a single sender. +func removeVideo(res http.ResponseWriter, req *http.Request) { if senders := peerConnection.GetSenders(); len(senders) != 0 { if err := peerConnection.RemoveTrack(senders[0]); err != nil { panic(err) } } - doSignaling(w, r) + doSignaling(res, req) fmt.Println("Video track has been removed") } @@ -130,18 +130,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -164,8 +165,8 @@ func main() { } // Read a video file from disk and write it to a webrtc.Track -// When the video has been completely read this exits without error -func writeVideoToTrack(t *webrtc.TrackLocalStaticSample) { +// When the video has been completely read this exits without error. +func writeVideoToTrack(track *webrtc.TrackLocalStaticSample) { // Open a IVF file and start reading using our IVFReader file, err := os.Open("output.ivf") if err != nil { @@ -183,17 +184,21 @@ func writeVideoToTrack(t *webrtc.TrackLocalStaticSample) { // It is important to use a time.Ticker instead of time.Sleep because // * avoids accumulating skew, just calling time.Sleep didn't compensate for the time spent parsing the data // * works around latency issues with Sleep (see https://github.com/golang/go/issues/44343) - ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)) + ticker := time.NewTicker( + time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000), + ) defer ticker.Stop() for ; true; <-ticker.C { frame, _, err := ivf.ParseNextFrame() if err != nil { fmt.Printf("Finish writing video track: %s ", err) + return } - if err = t.WriteSample(media.Sample{Data: frame, Duration: time.Second}); err != nil { + if err = track.WriteSample(media.Sample{Data: frame, Duration: time.Second}); err != nil { fmt.Printf("Finish writing video track: %s ", err) + return } } diff --git a/examples/play-from-disk/main.go b/examples/play-from-disk/main.go index 7b470427..20ece355 100644 --- a/examples/play-from-disk/main.go +++ b/examples/play-from-disk/main.go @@ -31,8 +31,7 @@ const ( oggPageDuration = time.Millisecond * 20 ) -// nolint:gocognit -func main() { +func main() { //nolint:gocognit,cyclop,gocyclo,maintidx // Assert that we have an audio or video file _, err := os.Stat(videoFileName) haveVideoFile := !os.IsNotExist(err) @@ -63,7 +62,7 @@ func main() { iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background()) - if haveVideoFile { + if haveVideoFile { //nolint:nestif file, openErr := os.Open(videoFileName) if openErr != nil { panic(openErr) @@ -88,7 +87,9 @@ func main() { } // Create a video track - videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: trackCodec}, "video", "pion") + videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample( + webrtc.RTPCodecCapability{MimeType: trackCodec}, "video", "pion", + ) if videoTrackErr != nil { panic(videoTrackErr) } @@ -131,7 +132,9 @@ func main() { // It is important to use a time.Ticker instead of time.Sleep because // * avoids accumulating skew, just calling time.Sleep didn't compensate for the time spent parsing the data // * works around latency issues with Sleep (see https://github.com/golang/go/issues/44343) - ticker := time.NewTicker(time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)) + ticker := time.NewTicker( + time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000), + ) defer ticker.Stop() for ; true; <-ticker.C { frame, _, ivfErr := ivf.ParseNextFrame() @@ -151,9 +154,11 @@ func main() { }() } - if haveAudioFile { + if haveAudioFile { //nolint:nestif // Create a audio track - audioTrack, audioTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "pion") + audioTrack, audioTrackErr := webrtc.NewTrackLocalStaticSample( + webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "pion", + ) if audioTrackErr != nil { panic(audioTrackErr) } @@ -233,18 +238,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -286,7 +292,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -303,10 +309,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -316,7 +323,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/reflect/main.go b/examples/reflect/main.go index f666cf17..04e5886c 100644 --- a/examples/reflect/main.go +++ b/examples/reflect/main.go @@ -22,18 +22,20 @@ import ( "github.com/pion/webrtc/v4" ) -// nolint:gocognit +// nolint:gocognit, cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} // Setup the codecs you want to use. // We'll use a VP8 and Opus but you can also define your own - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, webrtc.RTPCodecTypeVideo); err != nil { panic(err) } @@ -42,10 +44,10 @@ func main() { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Use the default set of Interceptors - if err := webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err := webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } @@ -57,10 +59,10 @@ func main() { if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration config := webrtc.Configuration{ @@ -82,7 +84,9 @@ func main() { }() // Create Track that we send video back to browser on - outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") + outputTrack, err := webrtc.NewTrackLocalStaticRTP( + webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion", + ) if err != nil { panic(err) } @@ -134,18 +138,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -178,7 +183,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -195,10 +200,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -208,7 +214,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/rtcp-processing/main.go b/examples/rtcp-processing/main.go index 5b5e61b2..f31086c6 100644 --- a/examples/rtcp-processing/main.go +++ b/examples/rtcp-processing/main.go @@ -101,7 +101,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -118,10 +118,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -131,7 +132,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/rtp-forwarder/main.go b/examples/rtp-forwarder/main.go index 12cd65d1..4d07417d 100644 --- a/examples/rtp-forwarder/main.go +++ b/examples/rtp-forwarder/main.go @@ -30,22 +30,25 @@ type udpConn struct { payloadType uint8 } -// nolint:gocognit -func main() { +func main() { //nolint:gocognit,cyclop,maintidx // Everything below is the Pion WebRTC API! Thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} // Setup the codecs you want to use. // We'll use a VP8 and Opus but you can also define your own - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, }, webrtc.RTPCodecTypeVideo); err != nil { panic(err) } - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, }, webrtc.RTPCodecTypeAudio); err != nil { panic(err) } @@ -54,7 +57,7 @@ func main() { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Register a intervalpli factory // This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender. @@ -64,15 +67,15 @@ func main() { if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Use the default set of Interceptors - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration config := webrtc.Configuration{ @@ -114,22 +117,22 @@ func main() { "audio": {port: 4000, payloadType: 111}, "video": {port: 4002, payloadType: 96}, } - for _, c := range udpConns { + for _, conn := range udpConns { // Create remote addr var raddr *net.UDPAddr - if raddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", c.port)); err != nil { + if raddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", conn.port)); err != nil { panic(err) } // Dial udp - if c.conn, err = net.DialUDP("udp", laddr, raddr); err != nil { + if conn.conn, err = net.DialUDP("udp", laddr, raddr); err != nil { panic(err) } defer func(conn net.PacketConn) { if closeErr := conn.Close(); closeErr != nil { panic(closeErr) } - }(c.conn) + }(conn.conn) } // Set a handler for when a new remote track starts, this handler will forward data to @@ -137,33 +140,33 @@ func main() { // In your application this is where you would handle/process audio/video peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { //nolint: revive // Retrieve udp connection - c, ok := udpConns[track.Kind().String()] + conn, ok := udpConns[track.Kind().String()] if !ok { return } - b := make([]byte, 1500) + buf := make([]byte, 1500) rtpPacket := &rtp.Packet{} for { // Read - n, _, readErr := track.Read(b) + n, _, readErr := track.Read(buf) if readErr != nil { panic(readErr) } // Unmarshal the packet and update the PayloadType - if err = rtpPacket.Unmarshal(b[:n]); err != nil { + if err = rtpPacket.Unmarshal(buf[:n]); err != nil { panic(err) } - rtpPacket.PayloadType = c.payloadType + rtpPacket.PayloadType = conn.payloadType // Marshal into original buffer with updated PayloadType - if n, err = rtpPacket.MarshalTo(b); err != nil { + if n, err = rtpPacket.MarshalTo(buf); err != nil { panic(err) } // Write - if _, writeErr := c.conn.Write(b[:n]); writeErr != nil { + if _, writeErr := conn.conn.Write(buf[:n]); writeErr != nil { // For this particular example, third party applications usually timeout after a short // amount of time during which the user doesn't have enough time to provide the answer // to the browser. @@ -191,18 +194,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Done forwarding") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Done forwarding") os.Exit(0) @@ -244,7 +248,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -261,10 +265,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -274,7 +279,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/rtp-to-webrtc/main.go b/examples/rtp-to-webrtc/main.go index 5b86f9ac..180f8a85 100644 --- a/examples/rtp-to-webrtc/main.go +++ b/examples/rtp-to-webrtc/main.go @@ -21,6 +21,7 @@ import ( "github.com/pion/webrtc/v4" ) +// nolint:cyclop func main() { peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ @@ -54,7 +55,9 @@ func main() { }() // Create a video track - videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") + videoTrack, err := webrtc.NewTrackLocalStaticRTP( + webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion", + ) if err != nil { panic(err) } @@ -137,7 +140,7 @@ func main() { } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -154,10 +157,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -167,7 +171,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/save-to-disk-av1/main.go b/examples/save-to-disk-av1/main.go index a985f637..8f8fccca 100644 --- a/examples/save-to-disk-av1/main.go +++ b/examples/save-to-disk-av1/main.go @@ -24,9 +24,9 @@ import ( "github.com/pion/webrtc/v4/pkg/media/ivfwriter" ) -func saveToDisk(i media.Writer, track *webrtc.TrackRemote) { +func saveToDisk(writer media.Writer, track *webrtc.TrackRemote) { defer func() { - if err := i.Close(); err != nil { + if err := writer.Close(); err != nil { panic(err) } }() @@ -35,26 +35,35 @@ func saveToDisk(i media.Writer, track *webrtc.TrackRemote) { rtpPacket, _, err := track.ReadRTP() if err != nil { fmt.Println(err) + return } - if err := i.WriteRTP(rtpPacket); err != nil { + if err := writer.WriteRTP(rtpPacket); err != nil { fmt.Println(err) + return } } } +// nolint:cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} // Setup the codecs you want to use. // We'll use a VP8 and Opus but you can also define your own - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeAV1, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeAV1, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 96, }, webrtc.RTPCodecTypeVideo); err != nil { panic(err) } @@ -63,7 +72,7 @@ func main() { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Register a intervalpli factory // This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender. @@ -73,15 +82,15 @@ func main() { if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Use the default set of Interceptors - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration config := webrtc.Configuration{} @@ -172,7 +181,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -189,10 +198,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -202,7 +212,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/save-to-disk/main.go b/examples/save-to-disk/main.go index a519aed2..48fd40b2 100644 --- a/examples/save-to-disk/main.go +++ b/examples/save-to-disk/main.go @@ -4,7 +4,8 @@ //go:build !js // +build !js -// save-to-disk is a simple application that shows how to record your webcam/microphone using Pion WebRTC and save VP8/Opus to disk. +// save-to-disk is a simple application that shows how to record your webcam/microphone using +// Pion WebRTC and save VP8/Opus to disk. package main import ( @@ -25,9 +26,9 @@ import ( "github.com/pion/webrtc/v4/pkg/media/oggwriter" ) -func saveToDisk(i media.Writer, track *webrtc.TrackRemote) { +func saveToDisk(writer media.Writer, track *webrtc.TrackRemote) { defer func() { - if err := i.Close(); err != nil { + if err := writer.Close(); err != nil { panic(err) } }() @@ -36,33 +37,39 @@ func saveToDisk(i media.Writer, track *webrtc.TrackRemote) { rtpPacket, _, err := track.ReadRTP() if err != nil { fmt.Println(err) + return } - if err := i.WriteRTP(rtpPacket); err != nil { + if err := writer.WriteRTP(rtpPacket); err != nil { fmt.Println(err) + return } } } -// nolint:gocognit +// nolint:gocognit, cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} // Setup the codecs you want to use. // We'll use a VP8 and Opus but you can also define your own - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, webrtc.RTPCodecTypeVideo); err != nil { panic(err) } - if err := m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 111, + if err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 111, }, webrtc.RTPCodecTypeAudio); err != nil { panic(err) } @@ -71,7 +78,7 @@ func main() { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Register a intervalpli factory // This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender. @@ -81,15 +88,15 @@ func main() { if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Use the default set of Interceptors - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration config := webrtc.Configuration{ @@ -200,7 +207,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -217,10 +224,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -230,7 +238,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/simulcast/main.go b/examples/simulcast/main.go index 6e15708a..bedd7333 100644 --- a/examples/simulcast/main.go +++ b/examples/simulcast/main.go @@ -22,7 +22,7 @@ import ( "github.com/pion/webrtc/v4" ) -// nolint:gocognit +// nolint:gocognit, cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. @@ -49,36 +49,52 @@ func main() { outputTracks := map[string]*webrtc.TrackLocalStaticRTP{} // Create Track that we send video back to browser on - outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_q", "pion_q") + outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + }, "video_q", "pion_q") if err != nil { panic(err) } outputTracks["q"] = outputTrack - outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_h", "pion_h") + outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + }, "video_h", "pion_h") if err != nil { panic(err) } outputTracks["h"] = outputTrack - outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_f", "pion_f") + outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + }, "video_f", "pion_f") if err != nil { panic(err) } outputTracks["f"] = outputTrack - if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo, webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionRecvonly}); err != nil { + if _, err = peerConnection.AddTransceiverFromKind( + webrtc.RTPCodecTypeVideo, + webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionRecvonly}, + ); err != nil { panic(err) } // Add this newly created track to the PeerConnection to send back video - if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["q"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { + if _, err = peerConnection.AddTransceiverFromTrack( + outputTracks["q"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { panic(err) } - if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["h"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { + if _, err = peerConnection.AddTransceiverFromTrack( + outputTracks["h"], + webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}, + ); err != nil { panic(err) } - if _, err = peerConnection.AddTransceiverFromTrack(outputTracks["f"], webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { + if _, err = peerConnection.AddTransceiverFromTrack( + outputTracks["f"], + webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}, + ); err != nil { panic(err) } @@ -117,7 +133,9 @@ func main() { defer ticker.Stop() for range ticker.C { fmt.Printf("Sending pli for stream with rid: %q, ssrc: %d\n", track.RID(), track.SSRC()) - if writeErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); writeErr != nil { + if writeErr := peerConnection.WriteRTCP( + []rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}, + ); writeErr != nil { fmt.Println(writeErr) } } @@ -138,18 +156,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -183,7 +202,7 @@ func main() { select {} } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -200,10 +219,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -213,7 +233,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/stats/main.go b/examples/stats/main.go index 7e6c6e2d..0e9e0994 100644 --- a/examples/stats/main.go +++ b/examples/stats/main.go @@ -24,17 +24,17 @@ import ( "github.com/pion/webrtc/v4" ) -// How ofter to print WebRTC stats +// How ofter to print WebRTC stats. const statsInterval = time.Second * 5 -// nolint:gocognit +// nolint:gocognit,cyclop func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} - if err := m.RegisterDefaultCodecs(); err != nil { + if err := mediaEngine.RegisterDefaultCodecs(); err != nil { panic(err) } @@ -42,7 +42,7 @@ func main() { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} statsInterceptorFactory, err := stats.NewInterceptor() if err != nil { @@ -53,15 +53,15 @@ func main() { statsInterceptorFactory.OnNewPeerConnection(func(_ string, g stats.Getter) { statsGetter = g }) - i.Add(statsInterceptorFactory) + interceptorRegistry.Add(statsInterceptorFactory) // Use the default set of Interceptors - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration config := webrtc.Configuration{ @@ -175,7 +175,7 @@ func main() { } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -192,10 +192,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -205,7 +206,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/swap-tracks/main.go b/examples/swap-tracks/main.go index a678dcdb..58115f83 100644 --- a/examples/swap-tracks/main.go +++ b/examples/swap-tracks/main.go @@ -24,6 +24,7 @@ import ( "github.com/pion/webrtc/v4" ) +// nolint: cyclop func main() { // nolint:gocognit // Everything below is the Pion WebRTC API! Thanks for using it ❤️. @@ -47,7 +48,9 @@ func main() { // nolint:gocognit }() // Create Track that we send video back to browser on - outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion") + outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + }, "video", "pion") if err != nil { panic(err) } @@ -115,12 +118,14 @@ func main() { // nolint:gocognit lastTimestamp = oldTimestamp // Check if this is the current track - if currTrack == trackNum { + if currTrack == trackNum { //nolint:nestif // If just switched to this track, send PLI to get picture refresh if !isCurrTrack { isCurrTrack = true if track.Kind() == webrtc.RTPCodecTypeVideo { - if writeErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); writeErr != nil { + if writeErr := peerConnection.WriteRTCP([]rtcp.Packet{ + &rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}, + }); writeErr != nil { fmt.Println(writeErr) } } @@ -136,17 +141,18 @@ func main() { // nolint:gocognit // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - peerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s\n", s.String()) + peerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. done() } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify done() } @@ -222,7 +228,7 @@ func main() { // nolint:gocognit } } -// Read from stdin until we get a newline +// Read from stdin until we get a newline. func readUntilNewline() (in string) { var err error @@ -239,10 +245,11 @@ func readUntilNewline() (in string) { } fmt.Println("") + return } -// JSON encode + base64 a SessionDescription +// JSON encode + base64 a SessionDescription. func encode(obj *webrtc.SessionDescription) string { b, err := json.Marshal(obj) if err != nil { @@ -252,7 +259,7 @@ func encode(obj *webrtc.SessionDescription) string { return base64.StdEncoding.EncodeToString(b) } -// Decode a base64 and unmarshal JSON into a SessionDescription +// Decode a base64 and unmarshal JSON into a SessionDescription. func decode(in string, obj *webrtc.SessionDescription) { b, err := base64.StdEncoding.DecodeString(in) if err != nil { diff --git a/examples/trickle-ice/main.go b/examples/trickle-ice/main.go index c0bbd8ea..b5af9ccc 100644 --- a/examples/trickle-ice/main.go +++ b/examples/trickle-ice/main.go @@ -15,7 +15,8 @@ import ( ) // websocketServer is called for every new inbound WebSocket -func websocketServer(ws *websocket.Conn) { // nolint:gocognit +// nolint: gocognit, cyclop +func websocketServer(wsConn *websocket.Conn) { // Create a new RTCPeerConnection peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{}) if err != nil { @@ -26,17 +27,17 @@ func websocketServer(ws *websocket.Conn) { // nolint:gocognit // ice trickle is implemented. Everytime we have a new candidate available we send // it as soon as it is ready. We don't wait to emit a Offer/Answer until they are // all available - peerConnection.OnICECandidate(func(c *webrtc.ICECandidate) { - if c == nil { + peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { return } - outbound, marshalErr := json.Marshal(c.ToJSON()) + outbound, marshalErr := json.Marshal(candidate.ToJSON()) if marshalErr != nil { panic(marshalErr) } - if _, err = ws.Write(outbound); err != nil { + if _, err = wsConn.Write(outbound); err != nil { panic(err) } }) @@ -61,7 +62,7 @@ func websocketServer(ws *websocket.Conn) { // nolint:gocognit buf := make([]byte, 1500) for { // Read each inbound WebSocket Message - n, err := ws.Read(buf) + n, err := wsConn.Read(buf) if err != nil { panic(err) } @@ -94,7 +95,7 @@ func websocketServer(ws *websocket.Conn) { // nolint:gocognit panic(marshalErr) } - if _, err = ws.Write(outbound); err != nil { + if _, err = wsConn.Write(outbound); err != nil { panic(err) } // Attempt to unmarshal as a ICECandidateInit. If the candidate field is empty diff --git a/examples/vnet/show-network-usage/main.go b/examples/vnet/show-network-usage/main.go index d7a12fb8..3d04b03e 100644 --- a/examples/vnet/show-network-usage/main.go +++ b/examples/vnet/show-network-usage/main.go @@ -38,6 +38,7 @@ import ( +--------------------+ +--------------------+ */ +// nolint:cyclop func main() { var inboundBytes int32 // for offerPeerConnection var outboundBytes int32 // for offerPeerConnection @@ -50,24 +51,25 @@ func main() { panicIfError(err) // Add a filter that monitors the traffic on the router - wan.AddChunkFilter(func(c vnet.Chunk) bool { - netType := c.SourceAddr().Network() + wan.AddChunkFilter(func(chunk vnet.Chunk) bool { + netType := chunk.SourceAddr().Network() if netType == "udp" { - dstAddr := c.DestinationAddr().String() + dstAddr := chunk.DestinationAddr().String() host, _, err2 := net.SplitHostPort(dstAddr) panicIfError(err2) if host == "1.2.3.4" { // c.UserData() returns a []byte of UDP payload - atomic.AddInt32(&inboundBytes, int32(len(c.UserData()))) + atomic.AddInt32(&inboundBytes, int32(len(chunk.UserData()))) //nolint:gosec // G115 } - srcAddr := c.SourceAddr().String() + srcAddr := chunk.SourceAddr().String() host, _, err2 = net.SplitHostPort(srcAddr) panicIfError(err2) if host == "1.2.3.4" { // c.UserData() returns a []byte of UDP payload - atomic.AddInt32(&outboundBytes, int32(len(c.UserData()))) + atomic.AddInt32(&outboundBytes, int32(len(chunk.UserData()))) //nolint:gosec // G115 } } + return true }) @@ -133,18 +135,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - offerPeerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (offerer)\n", s.String()) + offerPeerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (offerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -153,18 +156,19 @@ func main() { // Set the handler for Peer connection state // This will notify you when the peer has connected/disconnected - answerPeerConnection.OnConnectionStateChange(func(s webrtc.PeerConnectionState) { - fmt.Printf("Peer Connection State has changed: %s (answerer)\n", s.String()) + answerPeerConnection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + fmt.Printf("Peer Connection State has changed: %s (answerer)\n", state.String()) - if s == webrtc.PeerConnectionStateFailed { - // Wait until PeerConnection has had no network activity for 30 seconds or another failure. It may be reconnected using an ICE Restart. + if state == webrtc.PeerConnectionStateFailed { + // Wait until PeerConnection has had no network activity for 30 seconds or another failure. + // It may be reconnected using an ICE Restart. // Use webrtc.PeerConnectionStateDisconnected if you are interested in detecting faster timeout. // Note that the PeerConnection may come back from PeerConnectionStateDisconnected. fmt.Println("Peer Connection has gone to failed exiting") os.Exit(0) } - if s == webrtc.PeerConnectionStateClosed { + if state == webrtc.PeerConnectionStateClosed { // PeerConnection was explicitly closed. This usually happens from a DTLS CloseNotify fmt.Println("Peer Connection has gone to closed exiting") os.Exit(0) @@ -173,17 +177,17 @@ func main() { // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - answerPeerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - panicIfError(offerPeerConnection.AddICECandidate(i.ToJSON())) + answerPeerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + panicIfError(offerPeerConnection.AddICECandidate(candidate.ToJSON())) } }) // Set ICE Candidate handler. As soon as a PeerConnection has gathered a candidate // send it to the other peer - offerPeerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { - if i != nil { - panicIfError(answerPeerConnection.AddICECandidate(i.ToJSON())) + offerPeerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate != nil { + panicIfError(answerPeerConnection.AddICECandidate(candidate.ToJSON())) } }) diff --git a/examples/whip-whep/main.go b/examples/whip-whep/main.go index f5676e61..fc65060d 100644 --- a/examples/whip-whep/main.go +++ b/examples/whip-whep/main.go @@ -4,7 +4,8 @@ //go:build !js // +build !js -// whip-whep demonstrates how to use the WHIP/WHEP specifications to exchange SPD descriptions and stream media to a WebRTC client in the browser or OBS +// whip-whep demonstrates how to use the WHIP/WHEP specifications to exchange SPD descriptions +// and stream media to a WebRTC client in the browser or OBS. package main import ( @@ -34,7 +35,9 @@ var ( func main() { // Everything below is the Pion WebRTC API! Thanks for using it ❤️. var err error - if videoTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264}, "video", "pion"); err != nil { + if videoTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, + }, "video", "pion"); err != nil { panic(err) } @@ -46,21 +49,23 @@ func main() { panic(http.ListenAndServe(":8080", nil)) // nolint: gosec } -func whipHandler(w http.ResponseWriter, r *http.Request) { +func whipHandler(res http.ResponseWriter, req *http.Request) { // Read the offer from HTTP Request - offer, err := io.ReadAll(r.Body) + offer, err := io.ReadAll(req.Body) if err != nil { panic(err) } // Create a MediaEngine object to configure the supported codec - m := &webrtc.MediaEngine{} + mediaEngine := &webrtc.MediaEngine{} // Setup the codecs you want to use. // We'll only use H264 but you can also define your own - if err = m.RegisterCodec(webrtc.RTPCodecParameters{ - RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + if err = mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeH264, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, webrtc.RTPCodecTypeVideo); err != nil { panic(err) } @@ -69,7 +74,7 @@ func whipHandler(w http.ResponseWriter, r *http.Request) { // This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection` // this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry // for each PeerConnection. - i := &interceptor.Registry{} + interceptorRegistry := &interceptor.Registry{} // Register a intervalpli factory // This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender. @@ -79,15 +84,15 @@ func whipHandler(w http.ResponseWriter, r *http.Request) { if err != nil { panic(err) } - i.Add(intervalPliFactory) + interceptorRegistry.Add(intervalPliFactory) // Use the default set of Interceptors - if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil { + if err = webrtc.RegisterDefaultInterceptors(mediaEngine, interceptorRegistry); err != nil { panic(err) } // Create the API object with the MediaEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) // Prepare the configuration @@ -119,12 +124,12 @@ func whipHandler(w http.ResponseWriter, r *http.Request) { }) // Send answer via HTTP Response - writeAnswer(w, peerConnection, offer, "/whip") + writeAnswer(res, peerConnection, offer, "/whip") } -func whepHandler(w http.ResponseWriter, r *http.Request) { +func whepHandler(res http.ResponseWriter, req *http.Request) { // Read the offer from HTTP Request - offer, err := io.ReadAll(r.Body) + offer, err := io.ReadAll(req.Body) if err != nil { panic(err) } @@ -154,10 +159,10 @@ func whepHandler(w http.ResponseWriter, r *http.Request) { }() // Send answer via HTTP Response - writeAnswer(w, peerConnection, offer, "/whep") + writeAnswer(res, peerConnection, offer, "/whep") } -func writeAnswer(w http.ResponseWriter, peerConnection *webrtc.PeerConnection, offer []byte, path string) { +func writeAnswer(res http.ResponseWriter, peerConnection *webrtc.PeerConnection, offer []byte, path string) { // Set the handler for ICE connection state // This will notify you when the peer has connected/disconnected peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { @@ -168,7 +173,9 @@ func writeAnswer(w http.ResponseWriter, peerConnection *webrtc.PeerConnection, o } }) - if err := peerConnection.SetRemoteDescription(webrtc.SessionDescription{Type: webrtc.SDPTypeOffer, SDP: string(offer)}); err != nil { + if err := peerConnection.SetRemoteDescription(webrtc.SessionDescription{ + Type: webrtc.SDPTypeOffer, SDP: string(offer), + }); err != nil { panic(err) } @@ -189,9 +196,9 @@ func writeAnswer(w http.ResponseWriter, peerConnection *webrtc.PeerConnection, o <-gatherComplete // WHIP+WHEP expects a Location header and a HTTP Status Code of 201 - w.Header().Add("Location", path) - w.WriteHeader(http.StatusCreated) + res.Header().Add("Location", path) + res.WriteHeader(http.StatusCreated) // Write Answer with Candidates as HTTP Response - fmt.Fprint(w, peerConnection.LocalDescription().SDP) //nolint: errcheck + fmt.Fprint(res, peerConnection.LocalDescription().SDP) //nolint: errcheck } diff --git a/gathering_complete_promise.go b/gathering_complete_promise.go index 12d3170d..d51a9936 100644 --- a/gathering_complete_promise.go +++ b/gathering_complete_promise.go @@ -7,10 +7,12 @@ import ( "context" ) -// GatheringCompletePromise is a Pion specific helper function that returns a channel that is closed when gathering is complete. +// GatheringCompletePromise is a Pion specific helper function that returns a channel that is closed +// when gathering is complete. // This function may be helpful in cases where you are unable to trickle your ICE Candidates. // -// It is better to not use this function, and instead trickle candidates. If you use this function you will see longer connection startup times. +// It is better to not use this function, and instead trickle candidates. +// If you use this function you will see longer connection startup times. // When the call is connected you will see no impact however. func GatheringCompletePromise(pc *PeerConnection) (gatherComplete <-chan struct{}) { gatheringComplete, done := context.WithCancel(context.Background()) diff --git a/icecandidate.go b/icecandidate.go index 70802725..91cf6a1f 100644 --- a/icecandidate.go +++ b/icecandidate.go @@ -9,7 +9,7 @@ import ( "github.com/pion/ice/v4" ) -// ICECandidate represents a ice candidate +// ICECandidate represents a ice candidate. type ICECandidate struct { statsID string Foundation string `json:"foundation"` @@ -26,9 +26,12 @@ type ICECandidate struct { SDPMLineIndex uint16 `json:"sdpMLineIndex"` } -// Conversion for package ice - -func newICECandidatesFromICE(iceCandidates []ice.Candidate, sdpMid string, sdpMLineIndex uint16) ([]ICECandidate, error) { +// Conversion for package ice. +func newICECandidatesFromICE( + iceCandidates []ice.Candidate, + sdpMid string, + sdpMLineIndex uint16, +) ([]ICECandidate, error) { candidates := []ICECandidate{} for _, i := range iceCandidates { @@ -42,36 +45,36 @@ func newICECandidatesFromICE(iceCandidates []ice.Candidate, sdpMid string, sdpML return candidates, nil } -func newICECandidateFromICE(i ice.Candidate, sdpMid string, sdpMLineIndex uint16) (ICECandidate, error) { - typ, err := convertTypeFromICE(i.Type()) +func newICECandidateFromICE(candidate ice.Candidate, sdpMid string, sdpMLineIndex uint16) (ICECandidate, error) { + typ, err := convertTypeFromICE(candidate.Type()) if err != nil { return ICECandidate{}, err } - protocol, err := NewICEProtocol(i.NetworkType().NetworkShort()) + protocol, err := NewICEProtocol(candidate.NetworkType().NetworkShort()) if err != nil { return ICECandidate{}, err } - c := ICECandidate{ - statsID: i.ID(), - Foundation: i.Foundation(), - Priority: i.Priority(), - Address: i.Address(), + newCandidate := ICECandidate{ + statsID: candidate.ID(), + Foundation: candidate.Foundation(), + Priority: candidate.Priority(), + Address: candidate.Address(), Protocol: protocol, - Port: uint16(i.Port()), - Component: i.Component(), + Port: uint16(candidate.Port()), //nolint:gosec // G115 + Component: candidate.Component(), Typ: typ, - TCPType: i.TCPType().String(), + TCPType: candidate.TCPType().String(), SDPMid: sdpMid, SDPMLineIndex: sdpMLineIndex, } - if i.RelatedAddress() != nil { - c.RelatedAddress = i.RelatedAddress().Address - c.RelatedPort = uint16(i.RelatedAddress().Port) + if candidate.RelatedAddress() != nil { + newCandidate.RelatedAddress = candidate.RelatedAddress().Address + newCandidate.RelatedPort = uint16(candidate.RelatedAddress().Port) //nolint:gosec // G115 } - return c, nil + return newCandidate, nil } func (c ICECandidate) toICE() (ice.Candidate, error) { @@ -88,6 +91,7 @@ func (c ICECandidate) toICE() (ice.Candidate, error) { Foundation: c.Foundation, Priority: c.Priority, } + return ice.NewCandidateHost(&config) case ICECandidateTypeSrflx: config := ice.CandidateServerReflexiveConfig{ @@ -101,6 +105,7 @@ func (c ICECandidate) toICE() (ice.Candidate, error) { RelAddr: c.RelatedAddress, RelPort: int(c.RelatedPort), } + return ice.NewCandidateServerReflexive(&config) case ICECandidateTypePrflx: config := ice.CandidatePeerReflexiveConfig{ @@ -114,6 +119,7 @@ func (c ICECandidate) toICE() (ice.Candidate, error) { RelAddr: c.RelatedAddress, RelPort: int(c.RelatedPort), } + return ice.NewCandidatePeerReflexive(&config) case ICECandidateTypeRelay: config := ice.CandidateRelayConfig{ @@ -127,6 +133,7 @@ func (c ICECandidate) toICE() (ice.Candidate, error) { RelAddr: c.RelatedAddress, RelPort: int(c.RelatedPort), } + return ice.NewCandidateRelay(&config) default: return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ) @@ -153,6 +160,7 @@ func (c ICECandidate) String() string { if err != nil { return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err) } + return ic.String() } diff --git a/icecandidateinit.go b/icecandidateinit.go index 30ad93c0..bd9df800 100644 --- a/icecandidateinit.go +++ b/icecandidateinit.go @@ -3,7 +3,7 @@ package webrtc -// ICECandidateInit is used to serialize ice candidates +// ICECandidateInit is used to serialize ice candidates. type ICECandidateInit struct { Candidate string `json:"candidate"` SDPMid *string `json:"sdpMid"` diff --git a/icecandidatepair.go b/icecandidatepair.go index b87884bc..2ae2efe5 100644 --- a/icecandidatepair.go +++ b/icecandidatepair.go @@ -5,7 +5,7 @@ package webrtc import "fmt" -// ICECandidatePair represents an ICE Candidate pair +// ICECandidatePair represents an ICE Candidate pair. type ICECandidatePair struct { statsID string Local *ICECandidate @@ -21,9 +21,10 @@ func (p *ICECandidatePair) String() string { } // NewICECandidatePair returns an initialized *ICECandidatePair -// for the given pair of ICECandidate instances +// for the given pair of ICECandidate instances. func NewICECandidatePair(local, remote *ICECandidate) *ICECandidatePair { statsID := newICECandidatePairStatsID(local.statsID, remote.statsID) + return &ICECandidatePair{ statsID: statsID, Local: local, diff --git a/icecandidatetype.go b/icecandidatetype.go index 27d7a142..7ac62db1 100644 --- a/icecandidatetype.go +++ b/icecandidatetype.go @@ -13,7 +13,7 @@ import ( type ICECandidateType int const ( - // ICECandidateTypeUnknown is the enum's zero-value + // ICECandidateTypeUnknown is the enum's zero-value. ICECandidateTypeUnknown ICECandidateType = iota // ICECandidateTypeHost indicates that the candidate is of Host type as @@ -51,7 +51,7 @@ const ( iceCandidateTypeRelayStr = "relay" ) -// NewICECandidateType takes a string and converts it into ICECandidateType +// NewICECandidateType takes a string and converts it into ICECandidateType. func NewICECandidateType(raw string) (ICECandidateType, error) { switch raw { case iceCandidateTypeHostStr: @@ -95,6 +95,7 @@ func getCandidateType(candidateType ice.CandidateType) (ICECandidateType, error) default: // NOTE: this should never happen[tm] err := fmt.Errorf("%w: %s", errICEInvalidConvertCandidateType, candidateType.String()) + return ICECandidateTypeUnknown, err } } @@ -108,5 +109,6 @@ func (t ICECandidateType) MarshalText() ([]byte, error) { func (t *ICECandidateType) UnmarshalText(b []byte) error { var err error *t, err = NewICECandidateType(string(b)) + return err } diff --git a/icecomponent.go b/icecomponent.go index ae8f230f..ea13f985 100644 --- a/icecomponent.go +++ b/icecomponent.go @@ -8,7 +8,7 @@ package webrtc type ICEComponent int const ( - // ICEComponentUnknown is the enum's zero-value + // ICEComponentUnknown is the enum's zero-value. ICEComponentUnknown ICEComponent = iota // ICEComponentRTP indicates that the ICE Transport is used for RTP (or diff --git a/iceconnectionstate.go b/iceconnectionstate.go index 4b7deb80..488b882a 100644 --- a/iceconnectionstate.go +++ b/iceconnectionstate.go @@ -7,7 +7,7 @@ package webrtc type ICEConnectionState int const ( - // ICEConnectionStateUnknown is the enum's zero-value + // ICEConnectionStateUnknown is the enum's zero-value. ICEConnectionStateUnknown ICEConnectionState = iota // ICEConnectionStateNew indicates that any of the ICETransports are @@ -56,7 +56,7 @@ const ( iceConnectionStateClosedStr = "closed" ) -// NewICEConnectionState takes a string and converts it to ICEConnectionState +// NewICEConnectionState takes a string and converts it to ICEConnectionState. func NewICEConnectionState(raw string) ICEConnectionState { switch raw { case iceConnectionStateNewStr: diff --git a/icecredentialtype.go b/icecredentialtype.go index 5d704a9b..ae30d1d6 100644 --- a/icecredentialtype.go +++ b/icecredentialtype.go @@ -50,7 +50,7 @@ func (t ICECredentialType) String() string { } } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (t *ICECredentialType) UnmarshalJSON(b []byte) error { var val string if err := json.Unmarshal(b, &val); err != nil { @@ -63,10 +63,11 @@ func (t *ICECredentialType) UnmarshalJSON(b []byte) error { } *t = tmp + return nil } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (t ICECredentialType) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } diff --git a/icegatherer.go b/icegatherer.go index 9cb4acac..d1a6df6d 100644 --- a/icegatherer.go +++ b/icegatherer.go @@ -70,7 +70,7 @@ func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, error) { }, nil } -func (g *ICEGatherer) createAgent() error { +func (g *ICEGatherer) createAgent() error { //nolint:cyclop g.lock.Lock() defer g.lock.Unlock() @@ -149,11 +149,12 @@ func (g *ICEGatherer) createAgent() error { } g.agent = agent + return nil } // Gather ICE candidates. -func (g *ICEGatherer) Gather() error { +func (g *ICEGatherer) Gather() error { //nolint:cyclop if err := g.createAgent(); err != nil { return err } @@ -182,12 +183,13 @@ func (g *ICEGatherer) Gather() error { sdpMid = mid } - sdpMLineIndex := uint16(g.sdpMLineIndex.Load()) + sdpMLineIndex := uint16(g.sdpMLineIndex.Load()) //nolint:gosec // G115 if candidate != nil { c, err := newICECandidateFromICE(candidate, sdpMid, sdpMLineIndex) if err != nil { g.log.Warnf("Failed to convert ice.Candidate: %s", err) + return } onLocalCandidateHandler(&c) @@ -200,10 +202,11 @@ func (g *ICEGatherer) Gather() error { }); err != nil { return err } + return agent.GatherCandidates() } -// set media stream identification tag and media description index for this gatherer +// set media stream identification tag and media description index for this gatherer. func (g *ICEGatherer) setMediaStreamIdentification(mid string, mLineIndex uint16) { g.sdpMid.Store(mid) g.sdpMLineIndex.Store(uint32(mLineIndex)) @@ -290,7 +293,7 @@ func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) { sdpMid = mid } - sdpMLineIndex := uint16(g.sdpMLineIndex.Load()) + sdpMLineIndex := uint16(g.sdpMLineIndex.Load()) //nolint:gosec // G115 return newICECandidatesFromICE(iceCandidates, sdpMid, sdpMLineIndex) } @@ -301,7 +304,7 @@ func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) { g.onLocalCandidateHandler.Store(f) } -// OnStateChange fires any time the ICEGatherer changes +// OnStateChange fires any time the ICEGatherer changes. func (g *ICEGatherer) OnStateChange(f func(ICEGathererState)) { g.onStateChangeHandler.Store(f) } @@ -322,6 +325,7 @@ func (g *ICEGatherer) setState(s ICEGathererState) { func (g *ICEGatherer) getAgent() *ice.Agent { g.lock.RLock() defer g.lock.RUnlock() + return g.agent } @@ -339,6 +343,7 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) { stats, err := toICECandidatePairStats(candidatePairStats) if err != nil { g.log.Error(err.Error()) + continue } @@ -363,10 +368,10 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) { ID: candidateStats.ID, Type: StatsTypeLocalCandidate, IP: candidateStats.IP, - Port: int32(candidateStats.Port), + Port: int32(candidateStats.Port), //nolint:gosec // G115, no overflow, port Protocol: networkType.Protocol(), CandidateType: candidateType, - Priority: int32(candidateStats.Priority), + Priority: int32(candidateStats.Priority), //nolint:gosec URL: candidateStats.URL, RelayProtocol: candidateStats.RelayProtocol, Deleted: candidateStats.Deleted, @@ -391,10 +396,10 @@ func (g *ICEGatherer) collectStats(collector *statsReportCollector) { ID: candidateStats.ID, Type: StatsTypeRemoteCandidate, IP: candidateStats.IP, - Port: int32(candidateStats.Port), + Port: int32(candidateStats.Port), //nolint:gosec // G115, no overflow, port Protocol: networkType.Protocol(), CandidateType: candidateType, - Priority: int32(candidateStats.Priority), + Priority: int32(candidateStats.Priority), //nolint:gosec // G115 URL: candidateStats.URL, RelayProtocol: candidateStats.RelayProtocol, } @@ -418,6 +423,7 @@ func (g *ICEGatherer) getSelectedCandidatePairStats() (ICECandidatePairStats, bo stats, err := toICECandidatePairStats(selectedCandidatePairStats) if err != nil { g.log.Error(err.Error()) + return ICECandidatePairStats{}, false } diff --git a/icegatherer_test.go b/icegatherer_test.go index 6f7bd8db..ebe3a134 100644 --- a/icegatherer_test.go +++ b/icegatherer_test.go @@ -157,7 +157,7 @@ func TestICEGatherer_AlreadyClosed(t *testing.T) { }) } -func TestNewICEGathererSetMediaStreamIdentification(t *testing.T) { +func TestNewICEGathererSetMediaStreamIdentification(t *testing.T) { //nolint:cyclop // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 20) defer lim.Stop() diff --git a/icegathererstate.go b/icegathererstate.go index b78df5fc..26966dd8 100644 --- a/icegathererstate.go +++ b/icegathererstate.go @@ -11,7 +11,7 @@ import ( type ICEGathererState uint32 const ( - // ICEGathererStateUnknown is the enum's zero-value + // ICEGathererStateUnknown is the enum's zero-value. ICEGathererStateUnknown ICEGathererState = iota // ICEGathererStateNew indicates object has been created but diff --git a/icegatheringstate.go b/icegatheringstate.go index 13ea2f73..2878277b 100644 --- a/icegatheringstate.go +++ b/icegatheringstate.go @@ -7,7 +7,7 @@ package webrtc type ICEGatheringState int const ( - // ICEGatheringStateUnknown is the enum's zero-value + // ICEGatheringStateUnknown is the enum's zero-value. ICEGatheringStateUnknown ICEGatheringState = iota // ICEGatheringStateNew indicates that any of the ICETransports are @@ -31,7 +31,7 @@ const ( iceGatheringStateCompleteStr = "complete" ) -// NewICEGatheringState takes a string and converts it to ICEGatheringState +// NewICEGatheringState takes a string and converts it to ICEGatheringState. func NewICEGatheringState(raw string) ICEGatheringState { switch raw { case iceGatheringStateNewStr: diff --git a/iceprotocol.go b/iceprotocol.go index 67254f7d..8362e93d 100644 --- a/iceprotocol.go +++ b/iceprotocol.go @@ -13,7 +13,7 @@ import ( type ICEProtocol int const ( - // ICEProtocolUnknown is the enum's zero-value + // ICEProtocolUnknown is the enum's zero-value. ICEProtocolUnknown ICEProtocol = iota // ICEProtocolUDP indicates the URL uses a UDP transport. @@ -29,7 +29,7 @@ const ( iceProtocolTCPStr = "tcp" ) -// NewICEProtocol takes a string and converts it to ICEProtocol +// NewICEProtocol takes a string and converts it to ICEProtocol. func NewICEProtocol(raw string) (ICEProtocol, error) { switch { case strings.EqualFold(iceProtocolUDPStr, raw): diff --git a/icerole.go b/icerole.go index dce6336e..59ac9af5 100644 --- a/icerole.go +++ b/icerole.go @@ -8,7 +8,7 @@ package webrtc type ICERole int const ( - // ICERoleUnknown is the enum's zero-value + // ICERoleUnknown is the enum's zero-value. ICERoleUnknown ICERole = iota // ICERoleControlling indicates that the ICE agent that is responsible @@ -50,13 +50,14 @@ func (t ICERole) String() string { } } -// MarshalText implements encoding.TextMarshaler +// MarshalText implements encoding.TextMarshaler. func (t ICERole) MarshalText() ([]byte, error) { return []byte(t.String()), nil } -// UnmarshalText implements encoding.TextUnmarshaler +// UnmarshalText implements encoding.TextUnmarshaler. func (t *ICERole) UnmarshalText(b []byte) error { *t = newICERole(string(b)) + return nil } diff --git a/iceserver.go b/iceserver.go index a28e727e..32a36854 100644 --- a/iceserver.go +++ b/iceserver.go @@ -28,10 +28,11 @@ func (s ICEServer) parseURL(i int) (*stun.URI, error) { func (s ICEServer) validate() error { _, err := s.urls() + return err } -func (s ICEServer) urls() ([]*stun.URI, error) { +func (s ICEServer) urls() ([]*stun.URI, error) { //nolint:cyclop urls := []*stun.URI{} for i := range s.URLs { @@ -85,6 +86,7 @@ func iceserverUnmarshalUrls(val interface{}) (*[]string, error) { return nil, errInvalidICEServer } } + return &out, nil } @@ -101,14 +103,15 @@ func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) { if !ok { return nil, errInvalidICEServer } + return &OAuthCredential{ MACKey: MACKey, AccessToken: AccessToken, }, nil } -func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error { - if val, ok := m["urls"]; ok { +func (s *ICEServer) iceserverUnmarshalFields(fields map[string]interface{}) error { //nolint:cyclop + if val, ok := fields["urls"]; ok { u, err := iceserverUnmarshalUrls(val) if err != nil { return err @@ -118,13 +121,13 @@ func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error { s.URLs = []string{} } - if val, ok := m["username"]; ok { + if val, ok := fields["username"]; ok { s.Username, ok = val.(string) if !ok { return errInvalidICEServer } } - if val, ok := m["credentialType"]; ok { + if val, ok := fields["credentialType"]; ok { ct, ok := val.(string) if !ok { return errInvalidICEServer @@ -137,7 +140,7 @@ func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error { } else { s.CredentialType = ICECredentialTypePassword } - if val, ok := m["credential"]; ok { + if val, ok := fields["credential"]; ok { switch s.CredentialType { case ICECredentialTypePassword: s.Credential = val @@ -151,10 +154,11 @@ func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error { return errInvalidICECredentialTypeString } } + return nil } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (s *ICEServer) UnmarshalJSON(b []byte) error { var tmp interface{} err := json.Unmarshal(b, &tmp) @@ -164,10 +168,11 @@ func (s *ICEServer) UnmarshalJSON(b []byte) error { if m, ok := tmp.(map[string]interface{}); ok { return s.iceserverUnmarshalFields(m) } + return errInvalidICEServer } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (s ICEServer) MarshalJSON() ([]byte, error) { m := make(map[string]interface{}) m["urls"] = s.URLs @@ -178,5 +183,6 @@ func (s ICEServer) MarshalJSON() ([]byte, error) { m["credential"] = s.Credential } m["credentialType"] = s.CredentialType + return json.Marshal(m) } diff --git a/iceserver_test.go b/iceserver_test.go index 2071c602..6a8594c8 100644 --- a/iceserver_test.go +++ b/iceserver_test.go @@ -99,6 +99,7 @@ func TestICEServer_validate(t *testing.T) { } }) t.Run("JsonFailure", func(t *testing.T) { + //nolint:lll testCases := [][]byte{ []byte(`{"urls":"NOTAURL","username":"unittest","credential":"placeholder","credentialType":"password"}`), []byte(`{"urls":["turn:[2001:db8:1234:5678::1]?transport=udp"],"username":"unittest","credential":"placeholder","credentialType":"invalid"}`), diff --git a/icetransport.go b/icetransport.go index a78f8b76..e0603497 100644 --- a/icetransport.go +++ b/icetransport.go @@ -36,7 +36,6 @@ type ICETransport struct { conn *ice.Conn mux *mux.Mux - ctx context.Context ctxCancel func() loggerFactory logging.LoggerFactory @@ -45,7 +44,7 @@ type ICETransport struct { } // GetSelectedCandidatePair returns the selected candidate pair on which packets are sent -// if there is no selected pair nil is returned +// if there is no selected pair nil is returned. func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) { agent := t.gatherer.getAgent() if agent == nil { @@ -71,7 +70,7 @@ func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) { } // GetSelectedCandidatePairStats returns the selected candidate pair stats on which packets are sent -// if there is no selected pair empty stats, false is returned to indicate stats not available +// if there is no selected pair empty stats, false is returned to indicate stats not available. func (t *ICETransport) GetSelectedCandidatePairStats() (ICECandidatePairStats, bool) { return t.gatherer.getSelectedCandidatePairStats() } @@ -84,11 +83,12 @@ func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) log: loggerFactory.NewLogger("ortc"), } iceTransport.setState(ICETransportStateNew) + return iceTransport } // Start incoming connectivity checks based on its configured role. -func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *ICERole) error { +func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *ICERole) error { //nolint:cyclop t.lock.Lock() defer t.lock.Unlock() @@ -121,6 +121,7 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role * candidates, err := newICECandidatesFromICE([]ice.Candidate{local, remote}, "", 0) if err != nil { t.log.Warnf("%w: %s", errICECandiatesCoversionFailed, err) + return } t.onSelectedCandidatePairChange(NewICECandidatePair(&candidates[0], &candidates[1])) @@ -134,7 +135,8 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role * } t.role = *role - t.ctx, t.ctxCancel = context.WithCancel(context.Background()) + ctx, ctxCancel := context.WithCancel(context.Background()) + t.ctxCancel = ctxCancel // Drop the lock here to allow ICE candidates to be // added so that the agent can complete a connection @@ -144,12 +146,12 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role * var err error switch *role { case ICERoleControlling: - iceConn, err = agent.Dial(t.ctx, + iceConn, err = agent.Dial(ctx, params.UsernameFragment, params.Password) case ICERoleControlled: - iceConn, err = agent.Accept(t.ctx, + iceConn, err = agent.Accept(ctx, params.UsernameFragment, params.Password) @@ -171,7 +173,7 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role * config := mux.Config{ Conn: t.conn, - BufferSize: int(t.gatherer.api.settingEngine.getReceiveMTU()), + BufferSize: int(t.gatherer.api.settingEngine.getReceiveMTU()), //nolint:gosec // G115 LoggerFactory: t.loggerFactory, } t.mux = mux.NewMux(config) @@ -180,7 +182,7 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role * } // restart is not exposed currently because ORTC has users create a whole new ICETransport -// so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs +// so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs. func (t *ICETransport) restart() error { t.lock.Lock() defer t.lock.Unlock() @@ -190,9 +192,13 @@ func (t *ICETransport) restart() error { return fmt.Errorf("%w: unable to restart ICETransport", errICEAgentNotExist) } - if err := agent.Restart(t.gatherer.api.settingEngine.candidates.UsernameFragment, t.gatherer.api.settingEngine.candidates.Password); err != nil { + if err := agent.Restart( + t.gatherer.api.settingEngine.candidates.UsernameFragment, + t.gatherer.api.settingEngine.candidates.Password, + ); err != nil { return err } + return t.gatherer.Gather() } @@ -229,18 +235,21 @@ func (t *ICETransport) stop(shouldGracefullyClose bool) error { closeErrs = append(closeErrs, gatherer.GracefulClose()) } closeErrs = append(closeErrs, mux.Close()) + return util.FlattenErrs(closeErrs) } else if gatherer != nil { if shouldGracefullyClose { return gatherer.GracefulClose() } + return gatherer.Close() } + return nil } // OnSelectedCandidatePairChange sets a handler that is invoked when a new -// ICE candidate pair is selected +// ICE candidate pair is selected. func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandidatePair)) { t.onSelectedCandidatePairChangeHandler.Store(f) } @@ -308,8 +317,8 @@ func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error { defer t.lock.RUnlock() var ( - c ice.Candidate - err error + candidate ice.Candidate + err error ) if err = t.ensureGatherer(); err != nil { @@ -317,7 +326,7 @@ func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error { } if remoteCandidate != nil { - if c, err = remoteCandidate.toICE(); err != nil { + if candidate, err = remoteCandidate.toICE(); err != nil { return err } } @@ -327,7 +336,7 @@ func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error { return fmt.Errorf("%w: unable to add remote candidates", errICEAgentNotExist) } - return agent.AddRemoteCandidate(c) + return agent.AddRemoteCandidate(candidate) } // State returns the current ice transport state. @@ -335,6 +344,7 @@ func (t *ICETransport) State() ICETransportState { if v, ok := t.state.Load().(ICETransportState); ok { return v } + return ICETransportState(0) } @@ -355,6 +365,7 @@ func (t *ICETransport) setState(i ICETransportState) { func (t *ICETransport) newEndpoint(f mux.MatchFunc) *mux.Endpoint { t.lock.Lock() defer t.lock.Unlock() + return t.mux.NewEndpoint(f) } diff --git a/icetransportpolicy.go b/icetransportpolicy.go index d9a41637..39a1fa36 100644 --- a/icetransportpolicy.go +++ b/icetransportpolicy.go @@ -11,7 +11,7 @@ import ( // permitted candidates. Only these candidates are used for connectivity checks. type ICETransportPolicy int -// ICEGatherPolicy is the ORTC equivalent of ICETransportPolicy +// ICEGatherPolicy is the ORTC equivalent of ICETransportPolicy. type ICEGatherPolicy = ICETransportPolicy const ( @@ -29,7 +29,7 @@ const ( iceTransportPolicyAllStr = "all" ) -// NewICETransportPolicy takes a string and converts it to ICETransportPolicy +// NewICETransportPolicy takes a string and converts it to ICETransportPolicy. func NewICETransportPolicy(raw string) ICETransportPolicy { switch raw { case iceTransportPolicyRelayStr: @@ -50,17 +50,18 @@ func (t ICETransportPolicy) String() string { } } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (t *ICETransportPolicy) UnmarshalJSON(b []byte) error { var val string if err := json.Unmarshal(b, &val); err != nil { return err } *t = NewICETransportPolicy(val) + return nil } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (t ICETransportPolicy) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } diff --git a/icetransportstate.go b/icetransportstate.go index 645c5c77..714d5fe6 100644 --- a/icetransportstate.go +++ b/icetransportstate.go @@ -9,7 +9,7 @@ import "github.com/pion/ice/v4" type ICETransportState int const ( - // ICETransportStateUnknown is the enum's zero-value + // ICETransportStateUnknown is the enum's zero-value. ICETransportStateUnknown ICETransportState = iota // ICETransportStateNew indicates the ICETransport is waiting @@ -143,13 +143,14 @@ func (c ICETransportState) toICE() ice.ConnectionState { } } -// MarshalText implements encoding.TextMarshaler +// MarshalText implements encoding.TextMarshaler. func (c ICETransportState) MarshalText() ([]byte, error) { return []byte(c.String()), nil } -// UnmarshalText implements encoding.TextUnmarshaler +// UnmarshalText implements encoding.TextUnmarshaler. func (c *ICETransportState) UnmarshalText(b []byte) error { *c = newICETransportState(string(b)) + return nil } diff --git a/interceptor.go b/interceptor.go index b9e7f40d..d2df622f 100644 --- a/interceptor.go +++ b/interceptor.go @@ -37,7 +37,7 @@ func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry * return ConfigureTWCCSender(mediaEngine, interceptorRegistry) } -// ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports +// ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports. func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) error { reciver, err := report.NewReceiverInterceptor() if err != nil { @@ -51,6 +51,7 @@ func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) error { interceptorRegistry.Add(reciver) interceptorRegistry.Add(sender) + return nil } @@ -70,17 +71,22 @@ func ConfigureNack(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Re mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack", Parameter: "pli"}, RTPCodecTypeVideo) interceptorRegistry.Add(responder) interceptorRegistry.Add(generator) + return nil } // ConfigureTWCCHeaderExtensionSender will setup everything necessary for adding // a TWCC header extension to outgoing RTP packets. This will allow the remote peer to generate TWCC reports. func ConfigureTWCCHeaderExtensionSender(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo, + ); err != nil { return err } - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio, + ); err != nil { return err } @@ -90,6 +96,7 @@ func ConfigureTWCCHeaderExtensionSender(mediaEngine *MediaEngine, interceptorReg } interceptorRegistry.Add(i) + return nil } @@ -97,12 +104,16 @@ func ConfigureTWCCHeaderExtensionSender(mediaEngine *MediaEngine, interceptorReg // This must be called after registering codecs with the MediaEngine. func ConfigureTWCCSender(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error { mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBTransportCC}, RTPCodecTypeVideo) - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeVideo, + ); err != nil { return err } mediaEngine.RegisterFeedback(RTCPFeedback{Type: TypeRTCPFBTransportCC}, RTPCodecTypeAudio) - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.TransportCCURI}, RTPCodecTypeAudio, + ); err != nil { return err } @@ -112,6 +123,7 @@ func ConfigureTWCCSender(mediaEngine *MediaEngine, interceptorRegistry *intercep } interceptorRegistry.Add(generator) + return nil } @@ -125,20 +137,27 @@ func ConfigureCongestionControlFeedback(mediaEngine *MediaEngine, interceptorReg return err } interceptorRegistry.Add(generator) + return nil } -// ConfigureSimulcastExtensionHeaders enables the RTP Extension Headers needed for Simulcast +// ConfigureSimulcastExtensionHeaders enables the RTP Extension Headers needed for Simulcast. func ConfigureSimulcastExtensionHeaders(mediaEngine *MediaEngine) error { - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeVideo); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeVideo, + ); err != nil { return err } - if err := mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}, RTPCodecTypeVideo); err != nil { + if err := mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.SDESRTPStreamIDURI}, RTPCodecTypeVideo, + ); err != nil { return err } - return mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESRepairRTPStreamIDURI}, RTPCodecTypeVideo) + return mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.SDESRepairRTPStreamIDURI}, RTPCodecTypeVideo, + ) } type interceptorToTrackLocalWriter struct{ interceptor atomic.Value } // interceptor.RTPWriter } @@ -160,8 +179,14 @@ func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) { return i.WriteRTP(&packet.Header, packet.Payload) } -// nolint: unparam -func createStreamInfo(id string, ssrc, ssrcRTX, ssrcFEC SSRC, payloadType, payloadTypeRTX, payloadTypeFEC PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) *interceptor.StreamInfo { +//nolint:unparam +func createStreamInfo( + id string, + ssrc, ssrcRTX, ssrcFEC SSRC, + payloadType, payloadTypeRTX, payloadTypeFEC PayloadType, + codec RTPCodecCapability, + webrtcHeaderExtensions []RTPHeaderExtensionParameter, +) *interceptor.StreamInfo { headerExtensions := make([]interceptor.RTPHeaderExtension, 0, len(webrtcHeaderExtensions)) for _, h := range webrtcHeaderExtensions { headerExtensions = append(headerExtensions, interceptor.RTPHeaderExtension{ID: h.ID, URI: h.URI}) diff --git a/interceptor_test.go b/interceptor_test.go index 924bada7..a3540edc 100644 --- a/interceptor_test.go +++ b/interceptor_test.go @@ -26,7 +26,7 @@ import ( // E2E test of the features of Interceptors // * Assert an extension can be set on an outbound packet // * Assert an extension can be read on an outbound packet -// * Assert that attributes set by an interceptor are returned to the Reader +// * Assert that attributes set by an interceptor are returned to the Reader. func TestPeerConnection_Interceptor(t *testing.T) { to := test.TimeOut(time.Second * 20) defer to.Stop() @@ -40,14 +40,16 @@ func TestPeerConnection_Interceptor(t *testing.T) { NewInterceptorFn: func(_ string) (interceptor.Interceptor, error) { return &mock_interceptor.Interceptor{ BindLocalStreamFn: func(_ *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter { - return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { - // set extension on outgoing packet - header.Extension = true - header.ExtensionProfile = 0xBEDE - assert.NoError(t, header.SetExtension(2, []byte("foo"))) + return interceptor.RTPWriterFunc( + func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { + // set extension on outgoing packet + header.Extension = true + header.ExtensionProfile = 0xBEDE + assert.NoError(t, header.SetExtension(2, []byte("foo"))) - return writer.Write(header, payload, attributes) - }) + return writer.Write(header, payload, attributes) + }, + ) }, BindRemoteStreamFn: func(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) { @@ -56,6 +58,7 @@ func TestPeerConnection_Interceptor(t *testing.T) { } a.Set("attribute", "value") + return reader.Read(b, a) }) }, @@ -108,7 +111,7 @@ func TestPeerConnection_Interceptor(t *testing.T) { closePairNow(t, offerer, answerer) } -func Test_Interceptor_BindUnbind(t *testing.T) { +func Test_Interceptor_BindUnbind(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 10) defer lim.Stop() @@ -127,14 +130,17 @@ func Test_Interceptor_BindUnbind(t *testing.T) { mockInterceptor := &mock_interceptor.Interceptor{ BindRTCPReaderFn: func(reader interceptor.RTCPReader) interceptor.RTCPReader { atomic.AddUint32(&cntBindRTCPReader, 1) + return reader }, BindRTCPWriterFn: func(writer interceptor.RTCPWriter) interceptor.RTCPWriter { atomic.AddUint32(&cntBindRTCPWriter, 1) + return writer }, BindLocalStreamFn: func(_ *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter { atomic.AddUint32(&cntBindLocalStream, 1) + return writer }, UnbindLocalStreamFn: func(*interceptor.StreamInfo) { @@ -142,6 +148,7 @@ func Test_Interceptor_BindUnbind(t *testing.T) { }, BindRemoteStreamFn: func(_ *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader { atomic.AddUint32(&cntBindRemoteStream, 1) + return reader }, UnbindRemoteStreamFn: func(_ *interceptor.StreamInfo) { @@ -149,6 +156,7 @@ func Test_Interceptor_BindUnbind(t *testing.T) { }, CloseFn: func() error { atomic.AddUint32(&cntClose, 1) + return nil }, } @@ -225,6 +233,7 @@ func Test_InterceptorRegistry_Build(t *testing.T) { ir.Add(&mock_interceptor.Factory{ NewInterceptorFn: func(_ string) (interceptor.Interceptor, error) { registryBuildCount++ + return &interceptor.NoOp{}, nil }, }) @@ -274,6 +283,7 @@ func Test_Interceptor_ZeroSSRC(t *testing.T) { if nonMediaBandwidthProbe, ok := answerer.nonMediaBandwidthProbe.Load().(*RTPReceiver); ok { assert.Equal(t, len(nonMediaBandwidthProbe.Tracks()), 1) close(probeReceiverCreated) + return } } @@ -301,16 +311,18 @@ func TestInterceptorNack(t *testing.T) { t.Run("NoNack", func(t *testing.T) { testInterceptorNack(t, false) }) } -func testInterceptorNack(t *testing.T, requestNack bool) { +func testInterceptorNack(t *testing.T, requestNack bool) { //nolint:cyclop + t.Helper() + const numPackets = 20 ir := interceptor.Registry{} - m := MediaEngine{} + mediaEngine := MediaEngine{} var feedback []RTCPFeedback if requestNack { feedback = append(feedback, RTCPFeedback{"nack", ""}) } - err := m.RegisterCodec( + err := mediaEngine.RegisterCodec( RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{ "video/VP8", 90000, 0, @@ -323,7 +335,7 @@ func testInterceptorNack(t *testing.T, requestNack bool) { ) assert.NoError(t, err) api := NewAPI( - WithMediaEngine(&m), + WithMediaEngine(&mediaEngine), WithInterceptorRegistry(&ir), ) @@ -396,7 +408,7 @@ func testInterceptorNack(t *testing.T, requestNack bool) { } p, _, err2 := track2.ReadRTP() assert.NoError(t, err2) - assert.Equal(t, p.SequenceNumber, uint16(i)) + assert.Equal(t, p.SequenceNumber, uint16(i)) //nolint:gosec //G115 } close(done) }) @@ -411,8 +423,8 @@ func testInterceptorNack(t *testing.T, requestNack bool) { p.Version = 2 p.Marker = true p.PayloadType = 96 - p.SequenceNumber = uint16(i) - p.Timestamp = uint32(i * 90000 / 50) + p.SequenceNumber = uint16(i) //nolint:gosec // G115 + p.Timestamp = uint32(i * 90000 / 50) ///nolint:gosec // G115 p.Payload = []byte{42} err2 := track1.WriteRTP(&p) assert.NoError(t, err2) diff --git a/internal/fmtp/av1.go b/internal/fmtp/av1.go index 29eccd11..d7eab4d2 100644 --- a/internal/fmtp/av1.go +++ b/internal/fmtp/av1.go @@ -37,5 +37,6 @@ func (h *av1FMTP) Match(b FMTP) bool { func (h *av1FMTP) Parameter(key string) (string, bool) { v, ok := h.parameters[key] + return v, ok } diff --git a/internal/fmtp/fmtp.go b/internal/fmtp/fmtp.go index f515a648..95a3243d 100644 --- a/internal/fmtp/fmtp.go +++ b/internal/fmtp/fmtp.go @@ -25,7 +25,7 @@ func parseParameters(line string) map[string]string { } // FMTP interface for implementing custom -// FMTP parsers based on MimeType +// FMTP parsers based on MimeType. type FMTP interface { // MimeType returns the MimeType associated with // the fmtp @@ -38,36 +38,36 @@ type FMTP interface { Parameter(key string) (string, bool) } -// Parse parses an fmtp string based on the MimeType +// Parse parses an fmtp string based on the MimeType. func Parse(mimeType, line string) FMTP { - var f FMTP + var fmtp FMTP parameters := parseParameters(line) switch { case strings.EqualFold(mimeType, "video/h264"): - f = &h264FMTP{ + fmtp = &h264FMTP{ parameters: parameters, } case strings.EqualFold(mimeType, "video/vp9"): - f = &vp9FMTP{ + fmtp = &vp9FMTP{ parameters: parameters, } case strings.EqualFold(mimeType, "video/av1"): - f = &av1FMTP{ + fmtp = &av1FMTP{ parameters: parameters, } default: - f = &genericFMTP{ + fmtp = &genericFMTP{ mimeType: mimeType, parameters: parameters, } } - return f + return fmtp } type genericFMTP struct { @@ -80,24 +80,24 @@ func (g *genericFMTP) MimeType() string { } // Match returns true if g and b are compatible fmtp descriptions -// The generic implementation is used for MimeTypes that are not defined +// The generic implementation is used for MimeTypes that are not defined. func (g *genericFMTP) Match(b FMTP) bool { - c, ok := b.(*genericFMTP) + fmtp, ok := b.(*genericFMTP) if !ok { return false } - if !strings.EqualFold(g.mimeType, c.MimeType()) { + if !strings.EqualFold(g.mimeType, fmtp.MimeType()) { return false } for k, v := range g.parameters { - if vb, ok := c.parameters[k]; ok && !strings.EqualFold(vb, v) { + if vb, ok := fmtp.parameters[k]; ok && !strings.EqualFold(vb, v) { return false } } - for k, v := range c.parameters { + for k, v := range fmtp.parameters { if va, ok := g.parameters[k]; ok && !strings.EqualFold(va, v) { return false } @@ -108,5 +108,6 @@ func (g *genericFMTP) Match(b FMTP) bool { func (g *genericFMTP) Parameter(key string) (string, bool) { v, ok := g.parameters[key] + return v, ok } diff --git a/internal/fmtp/fmtp_test.go b/internal/fmtp/fmtp_test.go index 96c1a8e7..50927dd7 100644 --- a/internal/fmtp/fmtp_test.go +++ b/internal/fmtp/fmtp_test.go @@ -127,7 +127,7 @@ func TestParse(t *testing.T) { } } -func TestMatch(t *testing.T) { +func TestMatch(t *testing.T) { //nolint:maintidx consistString := map[bool]string{true: "consist", false: "inconsist"} for _, ca := range []struct { diff --git a/internal/fmtp/h264.go b/internal/fmtp/h264.go index b89b97aa..0fdaea55 100644 --- a/internal/fmtp/h264.go +++ b/internal/fmtp/h264.go @@ -16,6 +16,7 @@ func profileLevelIDMatches(a, b string) bool { if err != nil || len(bb) < 2 { return false } + return aa[0] == bb[0] && aa[1] == bb[1] } @@ -41,7 +42,7 @@ func (h *h264FMTP) MimeType() string { // apply for the level part of profile-level-id and does not apply // for the other stream properties and capability parameters. func (h *h264FMTP) Match(b FMTP) bool { - c, ok := b.(*h264FMTP) + fmtp, ok := b.(*h264FMTP) if !ok { return false } @@ -51,7 +52,7 @@ func (h *h264FMTP) Match(b FMTP) bool { if !hok { return false } - cpmode, cok := c.parameters["packetization-mode"] + cpmode, cok := fmtp.parameters["packetization-mode"] if !cok { return false } @@ -66,7 +67,7 @@ func (h *h264FMTP) Match(b FMTP) bool { return false } - cplid, cok := c.parameters["profile-level-id"] + cplid, cok := fmtp.parameters["profile-level-id"] if !cok { return false } @@ -80,5 +81,6 @@ func (h *h264FMTP) Match(b FMTP) bool { func (h *h264FMTP) Parameter(key string) (string, bool) { v, ok := h.parameters[key] + return v, ok } diff --git a/internal/fmtp/vp9.go b/internal/fmtp/vp9.go index 7fc618bc..bbbd7f29 100644 --- a/internal/fmtp/vp9.go +++ b/internal/fmtp/vp9.go @@ -37,5 +37,6 @@ func (h *vp9FMTP) Match(b FMTP) bool { func (h *vp9FMTP) Parameter(key string) (string, bool) { v, ok := h.parameters[key] + return v, ok } diff --git a/internal/mux/endpoint.go b/internal/mux/endpoint.go index 2a3d0357..d1a24c0b 100644 --- a/internal/mux/endpoint.go +++ b/internal/mux/endpoint.go @@ -20,7 +20,7 @@ type Endpoint struct { onClose func() } -// Close unregisters the endpoint from the Mux +// Close unregisters the endpoint from the Mux. func (e *Endpoint) Close() (err error) { if e.onClose != nil { e.onClose() @@ -31,6 +31,7 @@ func (e *Endpoint) Close() (err error) { } e.mux.RemoveEndpoint(e) + return nil } @@ -39,19 +40,20 @@ func (e *Endpoint) close() error { } // Read reads a packet of len(p) bytes from the underlying conn -// that are matched by the associated MuxFunc +// that are matched by the associated MuxFunc. func (e *Endpoint) Read(p []byte) (int, error) { return e.buffer.Read(p) } // ReadFrom reads a packet of len(p) bytes from the underlying conn -// that are matched by the associated MuxFunc +// that are matched by the associated MuxFunc. func (e *Endpoint) ReadFrom(p []byte) (int, net.Addr, error) { i, err := e.Read(p) + return i, nil, err } -// Write writes len(p) bytes to the underlying conn +// Write writes len(p) bytes to the underlying conn. func (e *Endpoint) Write(p []byte) (int, error) { n, err := e.mux.nextConn.Write(p) if errors.Is(err, ice.ErrNoCandidatePairs) { @@ -63,38 +65,38 @@ func (e *Endpoint) Write(p []byte) (int, error) { return n, err } -// WriteTo writes len(p) bytes to the underlying conn +// WriteTo writes len(p) bytes to the underlying conn. func (e *Endpoint) WriteTo(p []byte, _ net.Addr) (int, error) { return e.Write(p) } -// LocalAddr is a stub +// LocalAddr is a stub. func (e *Endpoint) LocalAddr() net.Addr { return e.mux.nextConn.LocalAddr() } -// RemoteAddr is a stub +// RemoteAddr is a stub. func (e *Endpoint) RemoteAddr() net.Addr { return e.mux.nextConn.RemoteAddr() } -// SetDeadline is a stub +// SetDeadline is a stub. func (e *Endpoint) SetDeadline(time.Time) error { return nil } -// SetReadDeadline is a stub +// SetReadDeadline is a stub. func (e *Endpoint) SetReadDeadline(time.Time) error { return nil } -// SetWriteDeadline is a stub +// SetWriteDeadline is a stub. func (e *Endpoint) SetWriteDeadline(time.Time) error { return nil } // SetOnClose is a user set callback that -// will be executed when `Close` is called +// will be executed when `Close` is called. func (e *Endpoint) SetOnClose(onClose func()) { e.onClose = onClose } diff --git a/internal/mux/mux.go b/internal/mux/mux.go index 02e3acae..94247689 100644 --- a/internal/mux/mux.go +++ b/internal/mux/mux.go @@ -19,19 +19,19 @@ const ( // The maximum amount of data that can be buffered before returning errors. maxBufferSize = 1000 * 1000 // 1MB - // How many total pending packets can be cached + // How many total pending packets can be cached. maxPendingPackets = 15 ) // Config collects the arguments to mux.Mux construction into -// a single structure +// a single structure. type Config struct { Conn net.Conn BufferSize int LoggerFactory logging.LoggerFactory } -// Mux allows multiplexing +// Mux allows multiplexing. type Mux struct { nextConn net.Conn bufferSize int @@ -45,9 +45,9 @@ type Mux struct { log logging.LeveledLogger } -// NewMux creates a new Mux +// NewMux creates a new Mux. func NewMux(config Config) *Mux { - m := &Mux{ + mux := &Mux{ nextConn: config.Conn, endpoints: make(map[*Endpoint]MatchFunc), bufferSize: config.BufferSize, @@ -55,31 +55,31 @@ func NewMux(config Config) *Mux { log: config.LoggerFactory.NewLogger("mux"), } - go m.readLoop() + go mux.readLoop() - return m + return mux } -// NewEndpoint creates a new Endpoint -func (m *Mux) NewEndpoint(f MatchFunc) *Endpoint { - e := &Endpoint{ +// NewEndpoint creates a new Endpoint. +func (m *Mux) NewEndpoint(matchFunc MatchFunc) *Endpoint { + endpoint := &Endpoint{ mux: m, buffer: packetio.NewBuffer(), } // Set a maximum size of the buffer in bytes. - e.buffer.SetLimitSize(maxBufferSize) + endpoint.buffer.SetLimitSize(maxBufferSize) m.lock.Lock() - m.endpoints[e] = f + m.endpoints[endpoint] = matchFunc m.lock.Unlock() - go m.handlePendingPackets(e, f) + go m.handlePendingPackets(endpoint, matchFunc) - return e + return endpoint } -// RemoveEndpoint removes an endpoint from the Mux +// RemoveEndpoint removes an endpoint from the Mux. func (m *Mux) RemoveEndpoint(e *Endpoint) { m.lock.Lock() defer m.lock.Unlock() @@ -92,6 +92,7 @@ func (m *Mux) Close() error { for e := range m.endpoints { if err := e.close(); err != nil { m.lock.Unlock() + return err } @@ -124,9 +125,11 @@ func (m *Mux) readLoop() { return case errors.Is(err, io.ErrShortBuffer), errors.Is(err, packetio.ErrTimeout): m.log.Errorf("mux: failed to read from packetio.Buffer %s", err.Error()) + continue case err != nil: m.log.Errorf("mux: ending readLoop packetio.Buffer error %s", err.Error()) + return } @@ -136,6 +139,7 @@ func (m *Mux) readLoop() { return } m.log.Errorf("mux: ending readLoop dispatch error %s", err.Error()) + return } } @@ -144,6 +148,7 @@ func (m *Mux) readLoop() { func (m *Mux) dispatch(buf []byte) error { if len(buf) == 0 { m.log.Warnf("Warning: mux: unable to dispatch zero length packet") + return nil } @@ -153,6 +158,7 @@ func (m *Mux) dispatch(buf []byte) error { for e, f := range m.endpoints { if f(buf) { endpoint = e + break } } @@ -161,12 +167,21 @@ func (m *Mux) dispatch(buf []byte) error { if !m.isClosed { if len(m.pendingPackets) >= maxPendingPackets { - m.log.Warnf("Warning: mux: no endpoint for packet starting with %d, not adding to queue size(%d)", buf[0], len(m.pendingPackets)) + m.log.Warnf( + "Warning: mux: no endpoint for packet starting with %d, not adding to queue size(%d)", + buf[0], //nolint:gosec // G602, false positive? + len(m.pendingPackets), + ) } else { - m.log.Warnf("Warning: mux: no endpoint for packet starting with %d, adding to queue size(%d)", buf[0], len(m.pendingPackets)) + m.log.Warnf( + "Warning: mux: no endpoint for packet starting with %d, adding to queue size(%d)", + buf[0], //nolint:gosec // G602, false positive? + len(m.pendingPackets), + ) m.pendingPackets = append(m.pendingPackets, append([]byte{}, buf...)) } } + return nil } @@ -176,6 +191,7 @@ func (m *Mux) dispatch(buf []byte) error { // Expected when bytes are received faster than the endpoint can process them (#2152, #2180) if errors.Is(err, packetio.ErrFull) { m.log.Infof("mux: endpoint buffer is full, dropping packet") + return nil } @@ -193,7 +209,7 @@ func (m *Mux) handlePendingPackets(endpoint *Endpoint, matchFunc MatchFunc) { m.log.Warnf("Warning: mux: error writing packet to endpoint from pending queue: %s", err) } } else { - pendingPackets = append(pendingPackets, buf) + pendingPackets = append(pendingPackets, buf) //nolint:makezero // todo fix } } m.pendingPackets = pendingPackets diff --git a/internal/mux/mux_test.go b/internal/mux/mux_test.go index 7adbed5c..4306184d 100644 --- a/internal/mux/mux_test.go +++ b/internal/mux/mux_test.go @@ -22,13 +22,13 @@ func TestNoEndpoints(t *testing.T) { ca, cb := net.Pipe() require.NoError(t, cb.Close()) - m := NewMux(Config{ + mux := NewMux(Config{ Conn: ca, BufferSize: testPipeBufferSize, LoggerFactory: logging.NewDefaultLoggerFactory(), }) - require.NoError(t, m.dispatch(make([]byte, 1))) - require.NoError(t, m.Close()) + require.NoError(t, mux.dispatch(make([]byte, 1))) + require.NoError(t, mux.Close()) require.NoError(t, ca.Close()) } @@ -37,7 +37,7 @@ type muxErrorConnReadResult struct { data []byte } -// muxErrorConn +// muxErrorConn. type muxErrorConn struct { net.Conn readResults []muxErrorConnReadResult @@ -49,6 +49,7 @@ func (m *muxErrorConn) Read(b []byte) (n int, err error) { n = len(m.readResults[0].data) m.readResults = m.readResults[1:] + return } @@ -81,13 +82,13 @@ func TestNonFatalRead(t *testing.T) { {io.EOF, nil}, }} - m := NewMux(Config{ + mux := NewMux(Config{ Conn: conn, BufferSize: testPipeBufferSize, LoggerFactory: logging.NewDefaultLoggerFactory(), }) - e := m.NewEndpoint(MatchAll) + e := mux.NewEndpoint(MatchAll) buff := make([]byte, testPipeBufferSize) n, err := e.Read(buff) @@ -98,24 +99,25 @@ func TestNonFatalRead(t *testing.T) { require.NoError(t, err) require.Equal(t, buff[:n], expectedData) - <-m.closedCh - require.NoError(t, m.Close()) + <-mux.closedCh + require.NoError(t, mux.Close()) require.NoError(t, ca.Close()) } // If a endpoint returns packetio.ErrFull it is a non-fatal error and shouldn't cause // the mux to be destroyed // pion/webrtc#2180 +// . func TestNonFatalDispatch(t *testing.T) { in, out := net.Pipe() - m := NewMux(Config{ + mux := NewMux(Config{ Conn: out, LoggerFactory: logging.NewDefaultLoggerFactory(), BufferSize: 1500, }) - e := m.NewEndpoint(MatchSRTP) + e := mux.NewEndpoint(MatchSRTP) e.buffer.SetLimitSize(1) for i := 0; i <= 25; i++ { @@ -124,19 +126,19 @@ func TestNonFatalDispatch(t *testing.T) { require.NoError(t, err) } - require.NoError(t, m.Close()) + require.NoError(t, mux.Close()) require.NoError(t, in.Close()) require.NoError(t, out.Close()) } func BenchmarkDispatch(b *testing.B) { - m := &Mux{ + mux := &Mux{ endpoints: make(map[*Endpoint]MatchFunc), log: logging.NewDefaultLoggerFactory().NewLogger("mux"), } - e := m.NewEndpoint(MatchSRTP) - m.NewEndpoint(MatchSRTCP) + endpoint := mux.NewEndpoint(MatchSRTP) + mux.NewEndpoint(MatchSRTCP) buf := []byte{128, 1, 2, 3, 4} buf2 := make([]byte, 1200) @@ -144,11 +146,11 @@ func BenchmarkDispatch(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - err := m.dispatch(buf) + err := mux.dispatch(buf) if err != nil { b.Errorf("dispatch: %v", err) } - _, err = e.buffer.Read(buf2) + _, err = endpoint.buffer.Read(buf2) if err != nil { b.Errorf("read: %v", err) } @@ -158,22 +160,22 @@ func BenchmarkDispatch(b *testing.B) { func TestPendingQueue(t *testing.T) { factory := logging.NewDefaultLoggerFactory() factory.DefaultLogLevel = logging.LogLevelDebug - m := &Mux{ + mux := &Mux{ endpoints: make(map[*Endpoint]MatchFunc), log: factory.NewLogger("mux"), } // Assert empty packets don't end up in queue - require.NoError(t, m.dispatch([]byte{})) - require.Equal(t, len(m.pendingPackets), 0) + require.NoError(t, mux.dispatch([]byte{})) + require.Equal(t, len(mux.pendingPackets), 0) // Test Happy Case inBuffer := []byte{20, 1, 2, 3, 4} outBuffer := make([]byte, len(inBuffer)) - require.NoError(t, m.dispatch(inBuffer)) + require.NoError(t, mux.dispatch(inBuffer)) - endpoint := m.NewEndpoint(MatchDTLS) + endpoint := mux.NewEndpoint(MatchDTLS) require.NotNil(t, endpoint) _, err := endpoint.Read(outBuffer) @@ -183,7 +185,7 @@ func TestPendingQueue(t *testing.T) { // Assert limit on pendingPackets for i := 0; i <= 100; i++ { - require.NoError(t, m.dispatch([]byte{64, 65, 66})) + require.NoError(t, mux.dispatch([]byte{64, 65, 66})) } - require.Equal(t, len(m.pendingPackets), maxPendingPackets) + require.Equal(t, len(mux.pendingPackets), maxPendingPackets) } diff --git a/internal/mux/muxfunc.go b/internal/mux/muxfunc.go index 69c3d14c..3f3e4292 100644 --- a/internal/mux/muxfunc.go +++ b/internal/mux/muxfunc.go @@ -3,20 +3,21 @@ package mux -// MatchFunc allows custom logic for mapping packets to an Endpoint +// MatchFunc allows custom logic for mapping packets to an Endpoint. type MatchFunc func([]byte) bool -// MatchAll always returns true +// MatchAll always returns true. func MatchAll([]byte) bool { return true } -// MatchRange returns true if the first byte of buf is in [lower..upper] +// MatchRange returns true if the first byte of buf is in [lower..upper]. func MatchRange(lower, upper byte, buf []byte) bool { if len(buf) < 1 { return false } b := buf[0] + return b >= lower && b <= upper } @@ -35,13 +36,13 @@ func MatchRange(lower, upper byte, buf []byte) bool { // +----------------+ // MatchDTLS is a MatchFunc that accepts packets with the first byte in [20..63] -// as defied in RFC7983 +// as defied in RFC7983. func MatchDTLS(b []byte) bool { return MatchRange(20, 63, b) } // MatchSRTPOrSRTCP is a MatchFunc that accepts packets with the first byte in [128..191] -// as defied in RFC7983 +// as defied in RFC7983. func MatchSRTPOrSRTCP(b []byte) bool { return MatchRange(128, 191, b) } @@ -51,15 +52,16 @@ func isRTCP(buf []byte) bool { if len(buf) < 4 { return false } + return buf[1] >= 192 && buf[1] <= 223 } -// MatchSRTP is a MatchFunc that only matches SRTP and not SRTCP +// MatchSRTP is a MatchFunc that only matches SRTP and not SRTCP. func MatchSRTP(buf []byte) bool { return MatchSRTPOrSRTCP(buf) && !isRTCP(buf) } -// MatchSRTCP is a MatchFunc that only matches SRTCP and not SRTP +// MatchSRTCP is a MatchFunc that only matches SRTCP and not SRTP. func MatchSRTCP(buf []byte) bool { return MatchSRTPOrSRTCP(buf) && isRTCP(buf) } diff --git a/internal/util/util.go b/internal/util/util.go index 966a6230..46703ef4 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -28,7 +28,7 @@ func RandUint32() uint32 { return globalMathRandomGenerator.Uint32() } -// FlattenErrs flattens multiple errors into one +// FlattenErrs flattens multiple errors into one. func FlattenErrs(errs []error) error { errs2 := []error{} for _, e := range errs { @@ -39,6 +39,7 @@ func FlattenErrs(errs []error) error { if len(errs2) == 0 { return nil } + return multiError(errs2) } @@ -71,5 +72,6 @@ func (me multiError) Is(err error) bool { } } } + return false } diff --git a/mediaengine.go b/mediaengine.go index 7a76917a..920f5c2e 100644 --- a/mediaengine.go +++ b/mediaengine.go @@ -117,8 +117,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", videoRTCPFeedback}, - PayloadType: 102, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + videoRTCPFeedback, + }, + PayloadType: 102, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=102", nil}, @@ -126,8 +130,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback}, - PayloadType: 104, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", + videoRTCPFeedback, + }, + PayloadType: 104, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=104", nil}, @@ -135,8 +143,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback}, - PayloadType: 106, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", + videoRTCPFeedback, + }, + PayloadType: 106, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=106", nil}, @@ -144,8 +156,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback}, - PayloadType: 108, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", + videoRTCPFeedback, + }, + PayloadType: 108, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=108", nil}, @@ -153,8 +169,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f", videoRTCPFeedback}, - PayloadType: 127, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f", + videoRTCPFeedback, + }, + PayloadType: 127, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=127", nil}, @@ -162,8 +182,13 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f", videoRTCPFeedback}, - PayloadType: 39, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, + 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f", + videoRTCPFeedback, + }, + PayloadType: 39, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=39", nil}, @@ -198,8 +223,12 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { }, { - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f", videoRTCPFeedback}, - PayloadType: 112, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, + "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f", + videoRTCPFeedback, + }, + PayloadType: 112, }, { RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=112", nil}, @@ -214,13 +243,14 @@ func (m *MediaEngine) RegisterDefaultCodecs() error { return nil } -// addCodec will append codec if it not exists +// addCodec will append codec if it not exists. func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPCodecParameters) []RTPCodecParameters { for _, c := range codecs { if c.MimeType == codec.MimeType && c.PayloadType == codec.PayloadType { return codecs } } + return append(codecs, codec) } @@ -240,12 +270,19 @@ func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType) default: return ErrUnknownType } + return nil } // RegisterHeaderExtension adds a header extension to the MediaEngine -// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete -func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapability, typ RTPCodecType, allowedDirections ...RTPTransceiverDirection) error { +// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete. +// +//nolint:cyclop +func (m *MediaEngine) RegisterHeaderExtension( + extension RTPHeaderExtensionCapability, + typ RTPCodecType, + allowedDirections ...RTPTransceiverDirection, +) error { m.mu.Lock() defer m.mu.Unlock() @@ -306,8 +343,11 @@ func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType) } // getHeaderExtensionID returns the negotiated ID for a header extension. -// If the Header Extension isn't enabled ok will be false -func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) (val int, audioNegotiated, videoNegotiated bool) { +// If the Header Extension isn't enabled ok will be false. +func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) ( + val int, + audioNegotiated, videoNegotiated bool, +) { m.mu.RLock() defer m.mu.RUnlock() @@ -325,7 +365,7 @@ func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapabilit } // copy copies any user modifiable state of the MediaEngine -// all internal state is reset +// all internal state is reset. func (m *MediaEngine) copy() *MediaEngine { m.mu.Lock() defer m.mu.Unlock() @@ -337,6 +377,7 @@ func (m *MediaEngine) copy() *MediaEngine { if len(m.headerExtensions) > 0 { cloned.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{} } + return cloned } @@ -346,6 +387,7 @@ func findCodecByPayload(codecs []RTPCodecParameters, payloadType PayloadType) *R return &codec } } + return nil } @@ -393,7 +435,7 @@ func (m *MediaEngine) collectStats(collector *statsReportCollector) { PayloadType: codec.PayloadType, MimeType: codec.MimeType, ClockRate: codec.ClockRate, - Channels: uint8(codec.Channels), + Channels: uint8(codec.Channels), //nolint:gosec // G115 SDPFmtpLine: codec.SDPFmtpLine, } @@ -405,15 +447,21 @@ func (m *MediaEngine) collectStats(collector *statsReportCollector) { statsLoop(m.audioCodecs) } -// Look up a codec and enable if it exists -func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType, exactMatches, partialMatches []RTPCodecParameters) (RTPCodecParameters, codecMatchType, error) { +// Look up a codec and enable if it exists. +// +//nolint:cyclop +func (m *MediaEngine) matchRemoteCodec( + remoteCodec RTPCodecParameters, + typ RTPCodecType, + exactMatches, partialMatches []RTPCodecParameters, +) (RTPCodecParameters, codecMatchType, error) { codecs := m.videoCodecs if typ == RTPCodecTypeAudio { codecs = m.audioCodecs } remoteFmtp := fmtp.Parse(remoteCodec.RTPCodecCapability.MimeType, remoteCodec.RTPCodecCapability.SDPFmtpLine) - if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt { + if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt { //nolint:nestif payloadType, err := strconv.ParseUint(apt, 10, 8) if err != nil { return RTPCodecParameters{}, codecMatchNone, err @@ -425,6 +473,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo if codec.PayloadType == PayloadType(payloadType) { aptMatch = codecMatchExact aptCodec = codec + break } } @@ -434,6 +483,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo if codec.PayloadType == PayloadType(payloadType) { aptMatch = codecMatchPartial aptCodec = codec + break } } @@ -446,22 +496,29 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo // replace the apt value with the original codec's payload type toMatchCodec := remoteCodec if aptMatched, mt := codecParametersFuzzySearch(aptCodec, codecs); mt == aptMatch { - toMatchCodec.SDPFmtpLine = strings.Replace(toMatchCodec.SDPFmtpLine, fmt.Sprintf("apt=%d", payloadType), fmt.Sprintf("apt=%d", aptMatched.PayloadType), 1) + toMatchCodec.SDPFmtpLine = strings.Replace( + toMatchCodec.SDPFmtpLine, + fmt.Sprintf("apt=%d", payloadType), + fmt.Sprintf("apt=%d", aptMatched.PayloadType), + 1, + ) } - // if apt's media codec is partial match, then apt codec must be partial match too + // if apt's media codec is partial match, then apt codec must be partial match too. localCodec, matchType := codecParametersFuzzySearch(toMatchCodec, codecs) if matchType == codecMatchExact && aptMatch == codecMatchPartial { matchType = codecMatchPartial } + return localCodec, matchType, nil } localCodec, matchType := codecParametersFuzzySearch(remoteCodec, codecs) + return localCodec, matchType, nil } -// Update header extensions from a remote media section +// Update header extensions from a remote media section. func (m *MediaEngine) updateHeaderExtensionFromMediaSection(media *sdp.MediaDescription) error { var typ RTPCodecType switch { @@ -482,10 +539,11 @@ func (m *MediaEngine) updateHeaderExtensionFromMediaSection(media *sdp.MediaDesc return err } } + return nil } -// Look up a header extension and enable if it exists +// Look up a header extension and enable if it exists. func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCodecType) error { if m.negotiatedHeaderExtensions == nil { return nil @@ -508,6 +566,7 @@ func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCod m.negotiatedHeaderExtensions[id] = h } } + return nil } @@ -521,8 +580,8 @@ func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPCodecType) } } -// Update the MediaEngine from a remote description -func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error { +// Update the MediaEngine from a remote description. +func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error { //nolint:cyclop m.mu.Lock() defer m.mu.Unlock() @@ -549,6 +608,7 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e if err := m.updateHeaderExtensionFromMediaSection(media); err != nil { return err } + continue } @@ -590,6 +650,7 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e return err } } + return nil } @@ -614,7 +675,8 @@ func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters { return nil } -func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters { //nolint:gocognit +//nolint:gocognit,cyclop +func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters { headerExtensions := make([]RTPHeaderExtensionParameter, 0) // perform before locking to prevent recursive RLocks @@ -622,21 +684,24 @@ func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPT m.mu.RLock() defer m.mu.RUnlock() - if m.negotiatedVideo && typ == RTPCodecTypeVideo || - m.negotiatedAudio && typ == RTPCodecTypeAudio { + + //nolint:nestif + if (m.negotiatedVideo && typ == RTPCodecTypeVideo) || (m.negotiatedAudio && typ == RTPCodecTypeAudio) { for id, e := range m.negotiatedHeaderExtensions { - if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) { + if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && + (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) { headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri}) } } } else { mediaHeaderExtensions := make(map[int]mediaEngineHeaderExtension) - for _, e := range m.headerExtensions { + for _, ext := range m.headerExtensions { usingNegotiatedID := false for id := range m.negotiatedHeaderExtensions { - if m.negotiatedHeaderExtensions[id].uri == e.uri { + if m.negotiatedHeaderExtensions[id].uri == ext.uri { usingNegotiatedID = true - mediaHeaderExtensions[id] = e + mediaHeaderExtensions[id] = ext + break } } @@ -647,7 +712,8 @@ func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPT idAvailable = false } if _, taken := m.negotiatedHeaderExtensions[id]; idAvailable && !taken { - mediaHeaderExtensions[id] = e + mediaHeaderExtensions[id] = ext + break } } @@ -655,7 +721,8 @@ func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPT } for id, e := range mediaHeaderExtensions { - if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) { + if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && + (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) { headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri}) } } diff --git a/mediaengine_test.go b/mediaengine_test.go index bab8b689..7f0ae392 100644 --- a/mediaengine_test.go +++ b/mediaengine_test.go @@ -18,6 +18,7 @@ import ( ) // pion/webrtc#1078 +// . func TestOpusCase(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) assert.NoError(t, err) @@ -33,6 +34,7 @@ func TestOpusCase(t *testing.T) { } // pion/example-webrtc-applications#89 +// . func TestVideoCase(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) assert.NoError(t, err) @@ -49,10 +51,11 @@ func TestVideoCase(t *testing.T) { assert.NoError(t, pc.Close()) } -func TestMediaEngineRemoteDescription(t *testing.T) { +func TestMediaEngineRemoteDescription(t *testing.T) { //nolint:maintidx mustParse := func(raw string) sdp.SessionDescription { s := sdp.SessionDescription{} assert.NoError(t, s.Unmarshal([]byte(raw))) + return s } @@ -62,12 +65,12 @@ o=- 4596489990601351948 2 IN IP4 127.0.0.1 s=- t=0 0 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(noMedia))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(noMedia))) - assert.False(t, m.negotiatedVideo) - assert.False(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.False(t, mediaEngine.negotiatedAudio) }) t.Run("Enable Opus", func(t *testing.T) { @@ -80,14 +83,14 @@ a=rtpmap:111 opus/48000/2 a=fmtp:111 minptime=10; useinbandfec=1 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(opusSamePayload))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - opusCodec, _, err := m.getCodecByPayload(111) + opusCodec, _, err := mediaEngine.getCodecByPayload(111) assert.NoError(t, err) assert.Equal(t, opusCodec.MimeType, MimeTypeOpus) }) @@ -102,17 +105,17 @@ a=rtpmap:112 opus/48000/2 a=fmtp:112 minptime=10; useinbandfec=1 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(opusSamePayload))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - _, _, err := m.getCodecByPayload(111) + _, _, err := mediaEngine.getCodecByPayload(111) assert.Error(t, err) - opusCodec, _, err := m.getCodecByPayload(112) + opusCodec, _, err := mediaEngine.getCodecByPayload(112) assert.NoError(t, err) assert.Equal(t, opusCodec.MimeType, MimeTypeOpus) }) @@ -127,14 +130,14 @@ a=rtpmap:96 opus/48000/2 a=fmtp:96 minptime=10; useinbandfec=1 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusSamePayload))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(opusSamePayload))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - opusCodec, _, err := m.getCodecByPayload(96) + opusCodec, _, err := mediaEngine.getCodecByPayload(96) assert.NoError(t, err) assert.Equal(t, opusCodec.MimeType, MimeTypeOpus) }) @@ -149,14 +152,14 @@ a=rtpmap:111 OPUS/48000/2 a=fmtp:111 minptime=10; useinbandfec=1 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusUpcase))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(opusUpcase))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - opusCodec, _, err := m.getCodecByPayload(111) + opusCodec, _, err := mediaEngine.getCodecByPayload(111) assert.NoError(t, err) assert.Equal(t, opusCodec.MimeType, "audio/OPUS") }) @@ -170,14 +173,14 @@ m=audio 9 UDP/TLS/RTP/SAVPF 111 a=rtpmap:111 opus/48000/2 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(opusNoFmtp))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(opusNoFmtp))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - opusCodec, _, err := m.getCodecByPayload(111) + opusCodec, _, err := mediaEngine.getCodecByPayload(111) assert.NoError(t, err) assert.Equal(t, opusCodec.MimeType, MimeTypeOpus) }) @@ -193,20 +196,26 @@ a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=rtpmap:111 opus/48000/2 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeAudio)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(headerExtensions))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: sdp.SDESMidURI}, RTPCodecTypeAudio), + ) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(headerExtensions))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - absID, absAudioEnabled, absVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}) + absID, absAudioEnabled, absVideoEnabled := mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}, + ) assert.Equal(t, absID, 0) assert.False(t, absAudioEnabled) assert.False(t, absVideoEnabled) - midID, midAudioEnabled, midVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI}) + midID, midAudioEnabled, midVideoEnabled := mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.SDESMidURI}, + ) assert.Equal(t, midID, 7) assert.True(t, midAudioEnabled) assert.False(t, midVideoEnabled) @@ -225,21 +234,29 @@ a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=rtpmap:111 opus/48000/2 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:mid"}, RTPCodecTypeAudio)) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, RTPCodecTypeAudio)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(headerExtensions))) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:mid"}, RTPCodecTypeAudio, + )) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{URI: "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"}, RTPCodecTypeAudio, + )) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(headerExtensions))) - assert.False(t, m.negotiatedVideo) - assert.True(t, m.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) - absID, absAudioEnabled, absVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}) + absID, absAudioEnabled, absVideoEnabled := mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}, + ) assert.Equal(t, absID, 0) assert.False(t, absAudioEnabled) assert.False(t, absVideoEnabled) - midID, midAudioEnabled, midVideoEnabled := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI}) + midID, midAudioEnabled, midVideoEnabled := mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.SDESMidURI}, + ) assert.Equal(t, midID, 7) assert.True(t, midAudioEnabled) assert.False(t, midVideoEnabled) @@ -256,21 +273,23 @@ a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f a=rtpmap:98 H264/90000 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, - PayloadType: 127, + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil, + }, + PayloadType: 127, }, RTPCodecTypeVideo)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - assert.True(t, m.negotiatedVideo) - assert.False(t, m.negotiatedAudio) + assert.True(t, mediaEngine.negotiatedVideo) + assert.False(t, mediaEngine.negotiatedAudio) - supportedH264, _, err := m.getCodecByPayload(98) + supportedH264, _, err := mediaEngine.getCodecByPayload(98) assert.NoError(t, err) assert.Equal(t, supportedH264.MimeType, MimeTypeH264) - _, _, err = m.getCodecByPayload(96) + _, _, err = mediaEngine.getCodecByPayload(96) assert.Error(t, err) }) @@ -283,14 +302,16 @@ m=video 60323 UDP/TLS/RTP/SAVPF 96 98 a=rtpmap:96 H264/90000 a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640c1f ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, - PayloadType: 127, + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil, + }, + PayloadType: 127, }, RTPCodecTypeVideo)) - assert.Error(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.Error(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - _, _, err := m.getCodecByPayload(96) + _, _, err := mediaEngine.getCodecByPayload(96) assert.Error(t, err) }) @@ -302,16 +323,16 @@ t=0 0 m=video 60323 UDP/TLS/RTP/SAVPF 96 a=rtpmap:96 VP9/90000 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", nil}, PayloadType: 98, }, RTPCodecTypeVideo)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - assert.True(t, m.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedVideo) - _, _, err := m.getCodecByPayload(96) + _, _, err := mediaEngine.getCodecByPayload(96) assert.NoError(t, err) }) @@ -323,16 +344,16 @@ t=0 0 m=video 60323 UDP/TLS/RTP/SAVPF 96 a=rtpmap:96 VP8/90000 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 96, }, RTPCodecTypeVideo)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - assert.True(t, m.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedVideo) - _, _, err := m.getCodecByPayload(96) + _, _, err := mediaEngine.getCodecByPayload(96) assert.NoError(t, err) }) @@ -358,64 +379,68 @@ a=fmtp:96 profile-id=2 a=rtpmap:97 rtx/90000 a=fmtp:97 apt=96 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 96, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil}, PayloadType: 97, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", nil}, - PayloadType: 102, + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", nil, + }, + PayloadType: 102, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=102", nil}, PayloadType: 103, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", nil}, - PayloadType: 104, + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", nil, + }, + PayloadType: 104, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=104", nil}, PayloadType: 105, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=2", nil}, PayloadType: 98, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=98", nil}, PayloadType: 99, }, RTPCodecTypeVideo)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - assert.True(t, m.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedVideo) - vp9Codec, _, err := m.getCodecByPayload(96) + vp9Codec, _, err := mediaEngine.getCodecByPayload(96) assert.NoError(t, err) assert.Equal(t, vp9Codec.MimeType, MimeTypeVP9) - vp9RTX, _, err := m.getCodecByPayload(97) + vp9RTX, _, err := mediaEngine.getCodecByPayload(97) assert.NoError(t, err) assert.Equal(t, vp9RTX.MimeType, MimeTypeRTX) - h264P1Codec, _, err := m.getCodecByPayload(106) + h264P1Codec, _, err := mediaEngine.getCodecByPayload(106) assert.NoError(t, err) assert.Equal(t, h264P1Codec.MimeType, MimeTypeH264) assert.Equal(t, h264P1Codec.SDPFmtpLine, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f") - h264P1RTX, _, err := m.getCodecByPayload(107) + h264P1RTX, _, err := mediaEngine.getCodecByPayload(107) assert.NoError(t, err) assert.Equal(t, h264P1RTX.MimeType, MimeTypeRTX) assert.Equal(t, h264P1RTX.SDPFmtpLine, "apt=106") - h264P0Codec, _, err := m.getCodecByPayload(108) + h264P0Codec, _, err := mediaEngine.getCodecByPayload(108) assert.NoError(t, err) assert.Equal(t, h264P0Codec.MimeType, MimeTypeH264) assert.Equal(t, h264P0Codec.SDPFmtpLine, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f") - h264P0RTX, _, err := m.getCodecByPayload(109) + h264P0RTX, _, err := mediaEngine.getCodecByPayload(109) assert.NoError(t, err) assert.Equal(t, h264P0RTX.MimeType, MimeTypeRTX) assert.Equal(t, h264P0RTX.SDPFmtpLine, "apt=108") @@ -433,24 +458,24 @@ a=fmtp:96 profile-id=2 a=rtpmap:97 rtx/90000 a=fmtp:97 apt=96 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 94, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=1", nil}, PayloadType: 96, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeRTX, 90000, 0, "apt=96", nil}, PayloadType: 97, }, RTPCodecTypeVideo)) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(profileLevels))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(profileLevels))) - assert.True(t, m.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedVideo) - _, _, err := m.getCodecByPayload(97) + _, _, err := mediaEngine.getCodecByPayload(97) assert.ErrorIs(t, err, ErrCodecNotFound) }) } @@ -468,52 +493,79 @@ func TestMediaEngineHeaderExtensionDirection(t *testing.T) { } t.Run("No Direction", func(t *testing.T) { - m := &MediaEngine{} - registerCodec(m) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio)) + mediaEngine := &MediaEngine{} + registerCodec(mediaEngine) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, + )) - params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) + params := mediaEngine.getRTPParametersByKind( + RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) assert.Equal(t, 1, len(params.HeaderExtensions)) }) t.Run("Same Direction", func(t *testing.T) { - m := &MediaEngine{} - registerCodec(m) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionRecvonly)) + mediaEngine := &MediaEngine{} + registerCodec(mediaEngine) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionRecvonly, + )) - params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) + params := mediaEngine.getRTPParametersByKind( + RTPCodecTypeAudio, + []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) assert.Equal(t, 1, len(params.HeaderExtensions)) }) t.Run("Different Direction", func(t *testing.T) { - m := &MediaEngine{} - registerCodec(m) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendonly)) + mediaEngine := &MediaEngine{} + registerCodec(mediaEngine) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendonly, + )) - params := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) + params := mediaEngine.getRTPParametersByKind( + RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) assert.Equal(t, 0, len(params.HeaderExtensions)) }) t.Run("Invalid Direction", func(t *testing.T) { - m := &MediaEngine{} - registerCodec(m) + mediaEngine := &MediaEngine{} + registerCodec(mediaEngine) - assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv), ErrRegisterHeaderExtensionInvalidDirection) - assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionInactive), ErrRegisterHeaderExtensionInvalidDirection) - assert.ErrorIs(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirection(0)), ErrRegisterHeaderExtensionInvalidDirection) + assert.ErrorIs(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionSendrecv, + ), ErrRegisterHeaderExtensionInvalidDirection) + assert.ErrorIs(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirectionInactive, + ), ErrRegisterHeaderExtensionInvalidDirection) + assert.ErrorIs(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio, RTPTransceiverDirection(0), + ), ErrRegisterHeaderExtensionInvalidDirection) }) t.Run("Unique extmapid with different codec", func(t *testing.T) { - m := &MediaEngine{} - registerCodec(m) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio)) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"pion-header-test2"}, RTPCodecTypeVideo)) + mediaEngine := &MediaEngine{} + registerCodec(mediaEngine) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test"}, RTPCodecTypeAudio), + ) + assert.NoError(t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"pion-header-test2"}, RTPCodecTypeVideo), + ) - audio := m.getRTPParametersByKind(RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) - video := m.getRTPParametersByKind(RTPCodecTypeVideo, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) + audio := mediaEngine.getRTPParametersByKind( + RTPCodecTypeAudio, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) + video := mediaEngine.getRTPParametersByKind( + RTPCodecTypeVideo, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) assert.Equal(t, 1, len(audio.HeaderExtensions)) assert.Equal(t, 1, len(video.HeaderExtensions)) @@ -521,23 +573,23 @@ func TestMediaEngineHeaderExtensionDirection(t *testing.T) { }) } -// If a user attempts to register a codec twice we should just discard duplicate calls +// If a user attempts to register a codec twice we should just discard duplicate calls. func TestMediaEngineDoubleRegister(t *testing.T) { - m := MediaEngine{} + mediaEngine := MediaEngine{} - assert.NoError(t, m.RegisterCodec( + assert.NoError(t, mediaEngine.RegisterCodec( RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil}, PayloadType: 111, }, RTPCodecTypeAudio)) - assert.NoError(t, m.RegisterCodec( + assert.NoError(t, mediaEngine.RegisterCodec( RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 0, "", nil}, PayloadType: 111, }, RTPCodecTypeAudio)) - assert.Equal(t, len(m.audioCodecs), 1) + assert.Equal(t, len(mediaEngine.audioCodecs), 1) } // The cloned MediaEngine instance should be able to update negotiated header extensions. @@ -569,6 +621,7 @@ func TestExtensionIdCollision(t *testing.T) { mustParse := func(raw string) sdp.SessionDescription { s := sdp.SessionDescription{} assert.NoError(t, s.Unmarshal([]byte(raw))) + return s } sdpSnippet := `v=0 @@ -582,36 +635,57 @@ a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=rtpmap:111 opus/48000/2 ` - m := MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{"urn:3gpp:video-orientation"}, RTPCodecTypeVideo)) + assert.NoError( + t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeVideo, + ), + ) + assert.NoError( + t, mediaEngine.RegisterHeaderExtension( + RTPHeaderExtensionCapability{"urn:3gpp:video-orientation"}, RTPCodecTypeVideo, + ), + ) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeAudio)) - assert.NoError(t, m.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.AudioLevelURI}, RTPCodecTypeAudio)) + assert.NoError( + t, mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.SDESMidURI}, RTPCodecTypeAudio), + ) + assert.NoError( + t, mediaEngine.RegisterHeaderExtension(RTPHeaderExtensionCapability{sdp.AudioLevelURI}, RTPCodecTypeAudio), + ) - assert.NoError(t, m.updateFromRemoteDescription(mustParse(sdpSnippet))) + assert.NoError(t, mediaEngine.updateFromRemoteDescription(mustParse(sdpSnippet))) - assert.True(t, m.negotiatedAudio) - assert.False(t, m.negotiatedVideo) + assert.True(t, mediaEngine.negotiatedAudio) + assert.False(t, mediaEngine.negotiatedVideo) - id, audioNegotiated, videoNegotiated := m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.ABSSendTimeURI}) + id, audioNegotiated, videoNegotiated := mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{ + sdp.ABSSendTimeURI, + }) assert.Equal(t, id, 0) assert.False(t, audioNegotiated) assert.False(t, videoNegotiated) - id, audioNegotiated, videoNegotiated = m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI}) + id, audioNegotiated, videoNegotiated = mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{ + sdp.SDESMidURI, + }) assert.Equal(t, id, 2) assert.True(t, audioNegotiated) assert.False(t, videoNegotiated) - id, audioNegotiated, videoNegotiated = m.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.AudioLevelURI}) + id, audioNegotiated, videoNegotiated = mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{ + sdp.AudioLevelURI, + }) assert.Equal(t, id, 1) assert.True(t, audioNegotiated) assert.False(t, videoNegotiated) - params := m.getRTPParametersByKind(RTPCodecTypeVideo, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) + params := mediaEngine.getRTPParametersByKind( + RTPCodecTypeVideo, + []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, + ) extensions := params.HeaderExtensions assert.Equal(t, 2, len(extensions)) @@ -689,12 +763,19 @@ a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001 for _, codec := range []RTPCodecParameters{ { - RTPCodecCapability: RTPCodecCapability{MimeType: mimeTypeVp8, ClockRate: 90000, RTCPFeedback: feedback}, - PayloadType: 96, + RTPCodecCapability: RTPCodecCapability{ + MimeType: mimeTypeVp8, ClockRate: 90000, RTCPFeedback: feedback, + }, + PayloadType: 96, }, { - RTPCodecCapability: RTPCodecCapability{MimeType: "video/h264", ClockRate: 90000, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", RTCPFeedback: feedback}, - PayloadType: 127, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/h264", + ClockRate: 90000, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + RTCPFeedback: feedback, + }, + PayloadType: 127, }, } { assert.NoError(t, me.RegisterCodec(codec, RTPCodecTypeVideo)) @@ -723,7 +804,7 @@ a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001 } } -// rtcp-fb should be an intersection of local and remote +// rtcp-fb should be an intersection of local and remote. func TestRTCPFeedbackHandling(t *testing.T) { const offerSdp = ` v=0 @@ -748,9 +829,11 @@ a=rtcp-fb:96 goog-remb a=rtcp-fb:96 nack ` - runTest := func(createTransceiver bool, t *testing.T) { - m := &MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + runTest := func(t *testing.T, createTransceiver bool) { + t.Helper() + + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, RTCPFeedback: []RTCPFeedback{ {Type: TypeRTCPFBTransportCC}, {Type: TypeRTCPFBNACK}, @@ -758,7 +841,7 @@ a=rtcp-fb:96 nack PayloadType: 96, }, RTPCodecTypeVideo)) - peerConnection, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + peerConnection, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) if createTransceiver { @@ -786,10 +869,10 @@ a=rtcp-fb:96 nack } t.Run("recvonly", func(t *testing.T) { - runTest(false, t) + runTest(t, false) }) t.Run("sendrecv", func(t *testing.T) { - runTest(true, t) + runTest(t, true) }) } diff --git a/networktype.go b/networktype.go index 7bf48623..a7ee773c 100644 --- a/networktype.go +++ b/networktype.go @@ -18,11 +18,11 @@ func supportedNetworkTypes() []NetworkType { } } -// NetworkType represents the type of network +// NetworkType represents the type of network. type NetworkType int const ( - // NetworkTypeUnknown is the enum's zero-value + // NetworkTypeUnknown is the enum's zero-value. NetworkTypeUnknown NetworkType = iota // NetworkTypeUDP4 indicates UDP over IPv4. @@ -61,7 +61,7 @@ func (t NetworkType) String() string { } } -// Protocol returns udp or tcp +// Protocol returns udp or tcp. func (t NetworkType) Protocol() string { switch t { case NetworkTypeUDP4: diff --git a/offeransweroptions.go b/offeransweroptions.go index 52868897..17435566 100644 --- a/offeransweroptions.go +++ b/offeransweroptions.go @@ -18,7 +18,7 @@ type AnswerOptions struct { } // OfferOptions structure describes the options used to control the offer -// creation process +// creation process. type OfferOptions struct { OfferAnswerOptions diff --git a/operations.go b/operations.go index 67d24eeb..3e65f135 100644 --- a/operations.go +++ b/operations.go @@ -8,7 +8,7 @@ import ( "sync" ) -// Operation is a function +// Operation is a function. type operation func() // Operations is a task executor. @@ -64,10 +64,11 @@ func (o *operations) tryEnqueue(op operation) bool { return true } -// IsEmpty checks if there are tasks in the queue +// IsEmpty checks if there are tasks in the queue. func (o *operations) IsEmpty() bool { o.mu.Lock() defer o.mu.Unlock() + return o.ops.Len() == 0 } @@ -93,6 +94,7 @@ func (o *operations) GracefulClose() { o.mu.Lock() if o.isClosed { o.mu.Unlock() + return } // do not enqueue anymore ops from here on @@ -120,6 +122,7 @@ func (o *operations) pop() func() { if op, ok := e.Value.(operation); ok { return op } + return nil } @@ -132,6 +135,7 @@ func (o *operations) start() { if o.ops.Len() == 0 || o.isClosed { o.busyCh = nil + return } diff --git a/peerconnection.go b/peerconnection.go index 5e613c22..07b188b7 100644 --- a/peerconnection.go +++ b/peerconnection.go @@ -100,6 +100,7 @@ type PeerConnection struct { // then call [(*API).NewPeerConnection] instead of this function. func NewPeerConnection(configuration Configuration) (*PeerConnection, error) { api := NewAPI() + return api.NewPeerConnection(configuration) } @@ -200,7 +201,7 @@ func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, // from its SetConfiguration counterpart because most of the checks do not // include verification statements related to the existing state. Thus the // function describes only minor verification of some the struct variables. -func (pc *PeerConnection) initConfiguration(configuration Configuration) error { +func (pc *PeerConnection) initConfiguration(configuration Configuration) error { //nolint:cyclop if configuration.PeerIdentity != "" { pc.configuration.PeerIdentity = configuration.PeerIdentity } @@ -255,7 +256,7 @@ func (pc *PeerConnection) initConfiguration(configuration Configuration) error { } // OnSignalingStateChange sets an event handler which is invoked when the -// peer connection's signaling state changes +// peer connection's signaling state changes. func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) { pc.mu.Lock() defer pc.mu.Unlock() @@ -282,7 +283,7 @@ func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) { } // OnNegotiationNeeded sets an event handler which is invoked when -// a change has occurred which requires session negotiation +// a change has occurred which requires session negotiation. func (pc *PeerConnection) OnNegotiationNeeded(f func()) { pc.onNegotiationNeededHandler.Store(f) } @@ -295,6 +296,7 @@ func (pc *PeerConnection) onNegotiationNeeded() { // connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort these steps. if !pc.ops.IsEmpty() { pc.updateNegotiationNeededFlagOnEmptyChain.set(true) + return } pc.ops.Enqueue(pc.negotiationNeededOp) @@ -312,6 +314,7 @@ func (pc *PeerConnection) negotiationNeededOp() { // true, and abort these steps. if !pc.ops.IsEmpty() { pc.updateNegotiationNeededFlagOnEmptyChain.set(true) + return } @@ -325,6 +328,7 @@ func (pc *PeerConnection) negotiationNeededOp() { // to false, and abort these steps. if !pc.checkNegotiationNeeded() { pc.isNegotiationNeeded.set(false) + return } @@ -342,7 +346,7 @@ func (pc *PeerConnection) negotiationNeededOp() { } } -func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit +func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit,cyclop // To check if negotiation is needed for connection, perform the following checks: // Skip 1, 2 steps // Step 3 @@ -364,23 +368,24 @@ func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit return true } - for _, t := range pc.rtpTransceivers { + for _, transceiver := range pc.rtpTransceivers { // https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag // Step 5.1 // if t.stopping && !t.stopped { // return true // } - m := getByMid(t.Mid(), localDesc) + mid := getByMid(transceiver.Mid(), localDesc) // Step 5.2 - if m == nil { + if mid == nil { return true } // Step 5.3.1 - if t.Direction() == RTPTransceiverDirectionSendrecv || t.Direction() == RTPTransceiverDirectionSendonly { - descMsid, okMsid := m.Attribute(sdp.AttrKeyMsid) - sender := t.Sender() + if transceiver.Direction() == RTPTransceiverDirectionSendrecv || + transceiver.Direction() == RTPTransceiverDirectionSendonly { + descMsid, okMsid := mid.Attribute(sdp.AttrKeyMsid) + sender := transceiver.Sender() if sender == nil { return true } @@ -400,17 +405,17 @@ func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit switch localDesc.Type { case SDPTypeOffer: // Step 5.3.2 - rm := getByMid(t.Mid(), remoteDesc) + rm := getByMid(transceiver.Mid(), remoteDesc) if rm == nil { return true } - if getPeerDirection(m) != t.Direction() && getPeerDirection(rm) != t.Direction().Revers() { + if getPeerDirection(mid) != transceiver.Direction() && getPeerDirection(rm) != transceiver.Direction().Revers() { return true } case SDPTypeAnswer: // Step 5.3.3 - if _, ok := m.Attribute(t.Direction().String()); !ok { + if _, ok := mid.Attribute(transceiver.Direction().String()); !ok { return true } default: @@ -491,7 +496,7 @@ func (pc *PeerConnection) onICEConnectionStateChange(cs ICEConnectionState) { } // OnConnectionStateChange sets an event handler which is called -// when the PeerConnectionState has changed +// when the PeerConnectionState has changed. func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) { pc.onConnectionStateChangeHandler.Store(f) } @@ -505,7 +510,7 @@ func (pc *PeerConnection) onConnectionStateChange(cs PeerConnectionState) { } // SetConfiguration updates the configuration of this PeerConnection object. -func (pc *PeerConnection) SetConfiguration(configuration Configuration) error { //nolint:gocognit +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() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} @@ -571,6 +576,7 @@ func (pc *PeerConnection) SetConfiguration(configuration Configuration) error { } pc.configuration.ICEServers = configuration.ICEServers } + return nil } @@ -586,11 +592,12 @@ func (pc *PeerConnection) GetConfiguration() Configuration { func (pc *PeerConnection) getStatsID() string { pc.mu.RLock() defer pc.mu.RUnlock() + return pc.statsID } // hasLocalDescriptionChanged returns whether local media (rtpTransceivers) has changed -// caller of this method should hold `pc.mu` lock +// caller of this method should hold `pc.mu` lock. func (pc *PeerConnection) hasLocalDescriptionChanged(desc *SessionDescription) bool { for _, t := range pc.rtpTransceivers { m := getByMid(t.Mid(), desc) @@ -602,12 +609,15 @@ func (pc *PeerConnection) hasLocalDescriptionChanged(desc *SessionDescription) b return true } } + return false } // CreateOffer starts the PeerConnection and generates the localDescription // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer -func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) { //nolint:gocognit +// +//nolint:gocognit,cyclop +func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) { useIdentity := pc.idpLoginURL != nil switch { case useIdentity: @@ -623,7 +633,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription } var ( - d *sdp.SessionDescription + descr *sdp.SessionDescription offer SessionDescription err error ) @@ -649,7 +659,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription } // include unmatched local transceivers - if !isPlanB { + if !isPlanB { //nolint:nestif // update the greater mid if the remote description provides a greater one if pc.currentRemoteDescription != nil { var numericMid int @@ -675,6 +685,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription pc.greaterMid = numericMid } } + continue } pc.greaterMid++ @@ -686,17 +697,22 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription } if pc.currentRemoteDescription == nil { - d, err = pc.generateUnmatchedSDP(currentTransceivers, useIdentity) + descr, err = pc.generateUnmatchedSDP(currentTransceivers, useIdentity) } else { - d, err = pc.generateMatchedSDP(currentTransceivers, useIdentity, true /*includeUnmatched */, connectionRoleFromDtlsRole(defaultDtlsRoleOffer)) + descr, err = pc.generateMatchedSDP( + currentTransceivers, + useIdentity, + true, /*includeUnmatched */ + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + ) } if err != nil { return SessionDescription{}, err } - updateSDPOrigin(&pc.sdpOrigin, d) - sdpBytes, err := d.Marshal() + updateSDPOrigin(&pc.sdpOrigin, descr) + sdpBytes, err := descr.Marshal() if err != nil { return SessionDescription{}, err } @@ -704,7 +720,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription offer = SessionDescription{ Type: SDPTypeOffer, SDP: string(sdpBytes), - parsed: d, + parsed: descr, } // Verify local media hasn't changed during offer @@ -719,6 +735,7 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription } pc.lastOffer = offer.SDP + return offer, nil } @@ -736,7 +753,12 @@ func (pc *PeerConnection) createICEGatherer() (*ICEGatherer, error) { // Update the PeerConnectionState given the state of relevant transports // https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum -func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnectionState, dtlsTransportState DTLSTransportState) { +// +//nolint:cyclop +func (pc *PeerConnection) updateConnectionState( + iceConnectionState ICEConnectionState, + dtlsTransportState DTLSTransportState, +) { connectionState := PeerConnectionStateNew switch { // The RTCPeerConnection object's [[IsClosed]] slot is true. @@ -766,7 +788,8 @@ func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnection // All RTCIceTransports and RTCDtlsTransports are in the "connected", "completed" or "closed" // state and all RTCDtlsTransports are in the "connected" or "closed" state. - case (iceConnectionState == ICEConnectionStateConnected || iceConnectionState == ICEConnectionStateCompleted || iceConnectionState == ICEConnectionStateClosed) && + case (iceConnectionState == ICEConnectionStateConnected || + iceConnectionState == ICEConnectionStateCompleted || iceConnectionState == ICEConnectionStateClosed) && (dtlsTransportState == DTLSTransportStateConnected || dtlsTransportState == DTLSTransportStateClosed): connectionState = PeerConnectionStateConnected } @@ -779,8 +802,8 @@ func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnection } func (pc *PeerConnection) createICETransport() *ICETransport { - t := pc.api.NewICETransport(pc.iceGatherer) - t.internalOnConnectionStateChangeHandler.Store(func(state ICETransportState) { + transport := pc.api.NewICETransport(pc.iceGatherer) + transport.internalOnConnectionStateChangeHandler.Store(func(state ICETransportState) { var cs ICEConnectionState switch state { case ICETransportStateNew: @@ -799,16 +822,19 @@ func (pc *PeerConnection) createICETransport() *ICETransport { cs = ICEConnectionStateClosed default: pc.log.Warnf("OnConnectionStateChange: unhandled ICE state: %s", state) + return } pc.onICEConnectionStateChange(cs) pc.updateConnectionState(cs, pc.dtlsTransport.State()) }) - return t + return transport } -// CreateAnswer starts the PeerConnection and generates the localDescription +// CreateAnswer starts the PeerConnection and generates the localDescription. +// +//nolint:cyclop func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, error) { useIdentity := pc.idpLoginURL != nil remoteDesc := pc.RemoteDescription() @@ -819,7 +845,8 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro return SessionDescription{}, errIdentityProviderNotImplemented case pc.isClosed.get(): return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} - case pc.signalingState.Get() != SignalingStateHaveRemoteOffer && pc.signalingState.Get() != SignalingStateHaveLocalPranswer: + case pc.signalingState.Get() != SignalingStateHaveRemoteOffer && + pc.signalingState.Get() != SignalingStateHaveLocalPranswer: return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState} } @@ -837,13 +864,13 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro pc.mu.Lock() defer pc.mu.Unlock() - d, err := pc.generateMatchedSDP(pc.rtpTransceivers, useIdentity, false /*includeUnmatched */, connectionRole) + descr, err := pc.generateMatchedSDP(pc.rtpTransceivers, useIdentity, false /*includeUnmatched */, connectionRole) if err != nil { return SessionDescription{}, err } - updateSDPOrigin(&pc.sdpOrigin, d) - sdpBytes, err := d.Marshal() + updateSDPOrigin(&pc.sdpOrigin, descr) + sdpBytes, err := descr.Marshal() if err != nil { return SessionDescription{}, err } @@ -851,19 +878,24 @@ func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, erro desc := SessionDescription{ Type: SDPTypeAnswer, SDP: string(sdpBytes), - parsed: d, + parsed: descr, } pc.lastAnswer = desc.SDP + return desc, nil } // 4.4.1.6 Set the SessionDescription -func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeOp) error { //nolint:gocognit +// +//nolint:gocognit,cyclop +func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeOp) error { switch { case pc.isClosed.get(): return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} case NewSDPType(sd.Type.String()) == SDPTypeUnknown: - return &rtcerr.TypeError{Err: fmt.Errorf("%w: '%d' is not a valid enum value of type SDPType", errPeerConnSDPTypeInvalidValue, sd.Type)} + return &rtcerr.TypeError{ + Err: fmt.Errorf("%w: '%d' is not a valid enum value of type SDPType", errPeerConnSDPTypeInvalidValue, sd.Type), + } } nextState, err := func() (SignalingState, error) { @@ -969,10 +1001,13 @@ func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeO } pc.onSignalingStateChange(nextState) } + return err } // SetLocalDescription sets the SessionDescription of the local peer +// +//nolint:cyclop func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { if pc.isClosed.get() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} @@ -1025,6 +1060,7 @@ func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { if pc.iceGatherer.State() == ICEGathererStateNew { return pc.iceGatherer.Gather() } + return nil } @@ -1036,11 +1072,14 @@ func (pc *PeerConnection) LocalDescription() *SessionDescription { if pendingLocalDescription := pc.PendingLocalDescription(); pendingLocalDescription != nil { return pendingLocalDescription } + return pc.CurrentLocalDescription() } // SetRemoteDescription sets the SessionDescription of the remote peer -func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { //nolint:gocognit,gocyclo +// +//nolint:gocognit,gocyclo,cyclop,maintidx +func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { if pc.isClosed.get() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -1063,7 +1102,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { sender.configureRTXAndFEC() } - var t *RTPTransceiver + var transceiver *RTPTransceiver localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...) detectedPlanB := descriptionIsPlanB(pc.RemoteDescription(), pc.log) if pc.configuration.SDPSemantics != SDPSemanticsUnifiedPlan { @@ -1072,7 +1111,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { weOffer := desc.Type == SDPTypeAnswer - if !weOffer && !detectedPlanB { + if !weOffer && !detectedPlanB { //nolint:nestif for _, media := range pc.RemoteDescription().parsed.MediaDescriptions { midValue := getMidValue(media) if midValue == "" { @@ -1089,17 +1128,17 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { continue } - t, localTransceivers = findByMid(midValue, localTransceivers) - if t == nil { - t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers) + transceiver, localTransceivers = findByMid(midValue, localTransceivers) + if transceiver == nil { + transceiver, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers) } else if direction == RTPTransceiverDirectionInactive { - if err := t.Stop(); err != nil { + if err := transceiver.Stop(); err != nil { return err } } switch { - case t == nil: + case transceiver == nil: receiver, err := pc.api.NewRTPReceiver(kind, pc.dtlsTransport) if err != nil { return err @@ -1112,44 +1151,47 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { localDirection = RTPTransceiverDirectionInactive } - t = newRTPTransceiver(receiver, nil, localDirection, kind, pc.api) + transceiver = newRTPTransceiver(receiver, nil, localDirection, kind, pc.api) pc.mu.Lock() - pc.addRTPTransceiver(t) + pc.addRTPTransceiver(transceiver) pc.mu.Unlock() // if transceiver is create by remote sdp, set prefer codec same as remote peer if codecs, err := codecsFromMediaDescription(media); err == nil { filteredCodecs := []RTPCodecParameters{} for _, codec := range codecs { - if c, matchType := codecParametersFuzzySearch(codec, pc.api.mediaEngine.getCodecsByKind(kind)); matchType == codecMatchExact { + if c, matchType := codecParametersFuzzySearch( + codec, + pc.api.mediaEngine.getCodecsByKind(kind), + ); matchType == codecMatchExact { // if codec match exact, use payloadtype register to mediaengine codec.PayloadType = c.PayloadType filteredCodecs = append(filteredCodecs, codec) } } - _ = t.SetCodecPreferences(filteredCodecs) + _ = transceiver.SetCodecPreferences(filteredCodecs) } case direction == RTPTransceiverDirectionRecvonly: - if t.Direction() == RTPTransceiverDirectionSendrecv { - t.setDirection(RTPTransceiverDirectionSendonly) - } else if t.Direction() == RTPTransceiverDirectionRecvonly { - t.setDirection(RTPTransceiverDirectionInactive) + if transceiver.Direction() == RTPTransceiverDirectionSendrecv { + transceiver.setDirection(RTPTransceiverDirectionSendonly) + } else if transceiver.Direction() == RTPTransceiverDirectionRecvonly { + transceiver.setDirection(RTPTransceiverDirectionInactive) } case direction == RTPTransceiverDirectionSendrecv: - if t.Direction() == RTPTransceiverDirectionSendonly { - t.setDirection(RTPTransceiverDirectionSendrecv) - } else if t.Direction() == RTPTransceiverDirectionInactive { - t.setDirection(RTPTransceiverDirectionRecvonly) + if transceiver.Direction() == RTPTransceiverDirectionSendonly { + transceiver.setDirection(RTPTransceiverDirectionSendrecv) + } else if transceiver.Direction() == RTPTransceiverDirectionInactive { + transceiver.setDirection(RTPTransceiverDirectionRecvonly) } case direction == RTPTransceiverDirectionSendonly: - if t.Direction() == RTPTransceiverDirectionInactive { - t.setDirection(RTPTransceiverDirectionRecvonly) + if transceiver.Direction() == RTPTransceiverDirectionInactive { + transceiver.setDirection(RTPTransceiverDirectionRecvonly) } } - if t.Mid() == "" { - if err := t.SetMid(midValue); err != nil { + if transceiver.Mid() == "" { + if err := transceiver.SetMid(midValue); err != nil { return err } } @@ -1193,6 +1235,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { pc.startRTP(true, &desc, currentTransceivers) }) } + return nil } @@ -1207,7 +1250,8 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { // If one of the agents is lite and the other one is not, the lite agent must be the controlled agent. // If both or neither agents are lite the offering agent is controlling. // RFC 8445 S6.1.1 - if (weOffer && remoteIsLite == pc.api.settingEngine.candidates.ICELite) || (remoteIsLite && !pc.api.settingEngine.candidates.ICELite) { + if (weOffer && remoteIsLite == pc.api.settingEngine.candidates.ICELite) || + (remoteIsLite && !pc.api.settingEngine.candidates.ICELite) { iceRole = ICERoleControlling } @@ -1223,11 +1267,19 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { } pc.ops.Enqueue(func() { - pc.startTransports(iceRole, dtlsRoleFromRemoteSDP(desc.parsed), iceDetails.Ufrag, iceDetails.Password, fingerprint, fingerprintHash) + pc.startTransports( + iceRole, + dtlsRoleFromRemoteSDP(desc.parsed), + iceDetails.Ufrag, + iceDetails.Password, + fingerprint, + fingerprintHash, + ) if weOffer { pc.startRTP(false, &desc, currentTransceivers) } }) + return nil } @@ -1247,16 +1299,18 @@ func (pc *PeerConnection) configureReceiver(incoming trackDetails, receiver *RTP func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPReceiver) { if err := receiver.startReceive(trackDetailsToRTPReceiveParameters(&incoming)); err != nil { pc.log.Warnf("RTPReceiver Receive failed %s", err) + return } - for _, t := range receiver.Tracks() { - if t.SSRC() == 0 || t.RID() != "" { + for _, track := range receiver.Tracks() { + if track.SSRC() == 0 || track.RID() != "" { return } if pc.api.settingEngine.fireOnTrackBeforeFirstRTP { - pc.onTrack(t, receiver) + pc.onTrack(track, receiver) + return } go func(track *TrackRemote) { @@ -1264,20 +1318,27 @@ func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPRece n, _, err := track.peek(b) if err != nil { pc.log.Warnf("Could not determine PayloadType for SSRC %d (%s)", track.SSRC(), err) + return } if err = track.checkAndUpdateTrack(b[:n]); err != nil { pc.log.Warnf("Failed to set codec settings for track SSRC %d (%s)", track.SSRC(), err) + return } pc.onTrack(track, receiver) - }(t) + }(track) } } -func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransceivers []*RTPTransceiver, weOffer bool) error { +//nolint:cyclop +func setRTPTransceiverCurrentDirection( + answer *SessionDescription, + currentTransceivers []*RTPTransceiver, + weOffer bool, +) error { currentTransceivers = append([]*RTPTransceiver{}, currentTransceivers...) for _, media := range answer.parsed.MediaDescriptions { midValue := getMidValue(media) @@ -1289,10 +1350,10 @@ func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransc continue } - var t *RTPTransceiver - t, currentTransceivers = findByMid(midValue, currentTransceivers) + var transceiver *RTPTransceiver + transceiver, currentTransceivers = findByMid(midValue, currentTransceivers) - if t == nil { + if transceiver == nil { return fmt.Errorf("%w: %q", errPeerConnTranscieverMidNil, midValue) } @@ -1315,19 +1376,20 @@ func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransc // If a transceiver is created by applying a remote description that has recvonly transceiver, // it will have no sender. In this case, the transceiver's current direction is set to inactive so // that the transceiver can be reused by next AddTrack. - if !weOffer && direction == RTPTransceiverDirectionSendonly && t.Sender() == nil { + if !weOffer && direction == RTPTransceiverDirectionSendonly && transceiver.Sender() == nil { direction = RTPTransceiverDirectionInactive } - t.setCurrentDirection(direction) + transceiver.setCurrentDirection(direction) } + return nil } func runIfNewReceiver( incomingTrack trackDetails, transceivers []*RTPTransceiver, - f func(incomingTrack trackDetails, receiver *RTPReceiver), + callbackFunc func(incomingTrack trackDetails, receiver *RTPReceiver), ) bool { for _, t := range transceivers { if t.Mid() != incomingTrack.mid { @@ -1342,52 +1404,61 @@ func runIfNewReceiver( continue } - f(incomingTrack, receiver) + callbackFunc(incomingTrack, receiver) + return true } return false } -// configureRTPReceivers opens knows inbound SRTP streams from the RemoteDescription -func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc *SessionDescription, currentTransceivers []*RTPTransceiver) { //nolint:gocognit +// configureRTPReceivers opens knows inbound SRTP streams from the RemoteDescription. +// +//nolint:gocognit,cyclop +func (pc *PeerConnection) configureRTPReceivers( + isRenegotiation bool, + remoteDesc *SessionDescription, + currentTransceivers []*RTPTransceiver, +) { incomingTracks := trackDetailsFromSDP(pc.log, remoteDesc.parsed) - if isRenegotiation { - for _, t := range currentTransceivers { - receiver := t.Receiver() + if isRenegotiation { //nolint:nestif + for _, transceiver := range currentTransceivers { + receiver := transceiver.Receiver() if receiver == nil { continue } - tracks := t.Receiver().Tracks() + tracks := transceiver.Receiver().Tracks() if len(tracks) == 0 { continue } - mid := t.Mid() + mid := transceiver.Mid() receiverNeedsStopped := false - for _, track := range tracks { - func(t *TrackRemote) { - t.mu.Lock() - defer t.mu.Unlock() + for _, trackRemote := range tracks { + func(track *TrackRemote) { + track.mu.Lock() + defer track.mu.Unlock() + + if track.rid != "" { + if details := trackDetailsForRID(incomingTracks, mid, track.rid); details != nil { + track.id = details.id + track.streamID = details.streamID - if t.rid != "" { - if details := trackDetailsForRID(incomingTracks, mid, t.rid); details != nil { - t.id = details.id - t.streamID = details.streamID return } - } else if t.ssrc != 0 { - if details := trackDetailsForSSRC(incomingTracks, t.ssrc); details != nil { - t.id = details.id - t.streamID = details.streamID + } else if track.ssrc != 0 { + if details := trackDetailsForSSRC(incomingTracks, track.ssrc); details != nil { + track.id = details.id + track.streamID = details.streamID + return } } receiverNeedsStopped = true - }(track) + }(trackRemote) } if !receiverNeedsStopped { @@ -1396,15 +1467,17 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc if err := receiver.Stop(); err != nil { pc.log.Warnf("Failed to stop RtpReceiver: %s", err) + continue } receiver, err := pc.api.NewRTPReceiver(receiver.kind, pc.dtlsTransport) if err != nil { pc.log.Warnf("Failed to create new RtpReceiver: %s", err) + continue } - t.setReceiver(receiver) + transceiver.setReceiver(receiver) } } @@ -1432,7 +1505,7 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc } } -// startRTPReceivers opens knows inbound SRTP streams from the RemoteDescription +// startRTPReceivers opens knows inbound SRTP streams from the RemoteDescription. func (pc *PeerConnection) startRTPReceivers(remoteDesc *SessionDescription, currentTransceivers []*RTPTransceiver) { incomingTracks := trackDetailsFromSDP(pc.log, remoteDesc.parsed) if len(incomingTracks) == 0 { @@ -1466,6 +1539,7 @@ func (pc *PeerConnection) startRTPReceivers(remoteDesc *SessionDescription, curr }) if err != nil { pc.log.Warnf("Could not add transceiver for remote SSRC %d: %s", incomingTrack.ssrcs[0], err) + continue } pc.configureReceiver(incomingTrack, t.Receiver()) @@ -1474,7 +1548,7 @@ func (pc *PeerConnection) startRTPReceivers(remoteDesc *SessionDescription, curr } } -// startRTPSenders starts all outbound RTP streams +// startRTPSenders starts all outbound RTP streams. func (pc *PeerConnection) startRTPSenders(currentTransceivers []*RTPTransceiver) error { for _, transceiver := range currentTransceivers { if sender := transceiver.Sender(); sender != nil && sender.isNegotiated() && !sender.hasSent() { @@ -1488,7 +1562,7 @@ func (pc *PeerConnection) startRTPSenders(currentTransceivers []*RTPTransceiver) return nil } -// Start SCTP subsystem +// Start SCTP subsystem. func (pc *PeerConnection) startSCTP() { // Start sctp if err := pc.sctpTransport.Start(SCTPCapabilities{ @@ -1503,7 +1577,11 @@ func (pc *PeerConnection) startSCTP() { } } -func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *SessionDescription) (handled bool, err error) { +//nolint:cyclop +func (pc *PeerConnection) handleUndeclaredSSRC( + ssrc SSRC, + remoteDescription *SessionDescription, +) (handled bool, err error) { if len(remoteDescription.parsed.MediaDescriptions) != 1 { return false, nil } @@ -1554,15 +1632,17 @@ func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *Ses pc.configureReceiver(incoming, t.Receiver()) pc.startReceiver(incoming, t.Receiver()) + return true, nil } // Chrome sends probing traffic on SSRC 0. This reads the packets to ensure that we properly -// generate TWCC reports for it. Since this isn't actually media we don't pass this to the user +// generate TWCC reports for it. Since this isn't actually media we don't pass this to the user. func (pc *PeerConnection) handleNonMediaBandwidthProbe() { nonMediaBandwidthProbe, err := pc.api.NewRTPReceiver(RTPCodecTypeVideo, pc.dtlsTransport) if err != nil { pc.log.Errorf("handleNonMediaBandwidthProbe failed to create RTPReceiver: %v", err) + return } @@ -1570,6 +1650,7 @@ func (pc *PeerConnection) handleNonMediaBandwidthProbe() { Encodings: []RTPDecodingParameters{{RTPCodingParameters: RTPCodingParameters{}}}, }); err != nil { pc.log.Errorf("handleNonMediaBandwidthProbe failed to start RTPReceiver: %v", err) + return } @@ -1578,12 +1659,13 @@ func (pc *PeerConnection) handleNonMediaBandwidthProbe() { for { if _, _, err = nonMediaBandwidthProbe.readRTP(b, nonMediaBandwidthProbe.Track()); err != nil { pc.log.Tracef("handleNonMediaBandwidthProbe read exiting: %v", err) + return } } } -func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) error { //nolint:gocognit +func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) error { //nolint:gocognit,cyclop remoteDescription := pc.RemoteDescription() if remoteDescription == nil { return errPeerConnRemoteDescriptionNil @@ -1606,17 +1688,23 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err return err } - midExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI}) + midExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.SDESMidURI}, + ) if !audioSupported && !videoSupported { return errPeerConnSimulcastMidRTPExtensionRequired } - streamIDExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESRTPStreamIDURI}) + streamIDExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.SDESRTPStreamIDURI}, + ) if !audioSupported && !videoSupported { return errPeerConnSimulcastStreamIDRTPExtensionRequired } - repairStreamIDExtensionID, _, _ := pc.api.mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESRepairRTPStreamIDURI}) + repairStreamIDExtensionID, _, _ := pc.api.mediaEngine.getHeaderExtensionID( + RTPHeaderExtensionCapability{sdp.SDESRepairRTPStreamIDURI}, + ) b := make([]byte, pc.api.settingEngine.getReceiveMTU()) @@ -1635,7 +1723,15 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err return err } - streamInfo := createStreamInfo("", ssrc, 0, 0, params.Codecs[0].PayloadType, 0, 0, params.Codecs[0].RTPCodecCapability, params.HeaderExtensions) + streamInfo := createStreamInfo( + "", + ssrc, + 0, 0, + params.Codecs[0].PayloadType, + 0, 0, + params.Codecs[0].RTPCodecCapability, + params.HeaderExtensions, + ) readStream, interceptor, rtcpReadStream, rtcpInterceptor, err := pc.dtlsTransport.streamsForSSRC(ssrc, *streamInfo) if err != nil { return err @@ -1655,7 +1751,14 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err return err } - if _, paddingOnly, err = handleUnknownRTPPacket(b[:i], uint8(midExtensionID), uint8(streamIDExtensionID), uint8(repairStreamIDExtensionID), &mid, &rid, &rsid); err != nil { + if _, paddingOnly, err = handleUnknownRTPPacket( + b[:i], uint8(midExtensionID), //nolint:gosec // G115 + uint8(streamIDExtensionID), //nolint:gosec // G115 + uint8(repairStreamIDExtensionID), //nolint:gosec // G115 + &mid, + &rid, + &rsid, + ); err != nil { return err } @@ -1671,46 +1774,60 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err if rsid != "" { receiver.mu.Lock() defer receiver.mu.Unlock() + return receiver.receiveForRtx(SSRC(0), rsid, streamInfo, readStream, interceptor, rtcpReadStream, rtcpInterceptor) } - track, err := receiver.receiveForRid(rid, params, streamInfo, readStream, interceptor, rtcpReadStream, rtcpInterceptor) + track, err := receiver.receiveForRid( + rid, + params, + streamInfo, + readStream, + interceptor, + rtcpReadStream, + rtcpInterceptor, + ) if err != nil { return err } pc.onTrack(track, receiver) + return nil } } pc.api.interceptor.UnbindRemoteStream(streamInfo) + return errPeerConnSimulcastIncomingSSRCFailed } -// undeclaredMediaProcessor handles RTP/RTCP packets that don't match any a:ssrc lines +// undeclaredMediaProcessor handles RTP/RTCP packets that don't match any a:ssrc lines. func (pc *PeerConnection) undeclaredMediaProcessor() { go pc.undeclaredRTPMediaProcessor() go pc.undeclaredRTCPMediaProcessor() } -func (pc *PeerConnection) undeclaredRTPMediaProcessor() { +func (pc *PeerConnection) undeclaredRTPMediaProcessor() { //nolint:cyclop var simulcastRoutineCount uint64 for { srtpSession, err := pc.dtlsTransport.getSRTPSession() if err != nil { pc.log.Warnf("undeclaredMediaProcessor failed to open SrtpSession: %v", err) + return } srtcpSession, err := pc.dtlsTransport.getSRTCPSession() if err != nil { pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err) + return } srtpReadStream, ssrc, err := srtpSession.AcceptStream() if err != nil { pc.log.Warnf("Failed to accept RTP %v", err) + return } @@ -1718,6 +1835,7 @@ func (pc *PeerConnection) undeclaredRTPMediaProcessor() { srtcpReadStream, err := srtcpSession.OpenReadStream(ssrc) if err != nil { pc.log.Warnf("Failed to open RTCP stream for %d: %v", ssrc, err) + return } @@ -1728,6 +1846,7 @@ func (pc *PeerConnection) undeclaredRTPMediaProcessor() { if err = srtcpReadStream.Close(); err != nil { pc.log.Warnf("Failed to close RTCP stream %v", err) } + continue } @@ -1735,12 +1854,14 @@ func (pc *PeerConnection) undeclaredRTPMediaProcessor() { if ssrc == 0 { go pc.handleNonMediaBandwidthProbe() + continue } if atomic.AddUint64(&simulcastRoutineCount, 1) >= simulcastMaxProbeRoutines { atomic.AddUint64(&simulcastRoutineCount, ^uint64(0)) pc.log.Warn(ErrSimulcastProbeOverflow.Error()) + continue } @@ -1764,12 +1885,14 @@ func (pc *PeerConnection) undeclaredRTCPMediaProcessor() { srtcpSession, err := pc.dtlsTransport.getSRTCPSession() if err != nil { pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err) + return } stream, ssrc, err := srtcpSession.AcceptStream() if err != nil { pc.log.Warnf("Failed to accept RTCP %v", err) + return } pc.log.Warnf("Incoming unhandled RTCP ssrc(%d), OnTrack will not be fired", ssrc) @@ -1788,6 +1911,7 @@ func (pc *PeerConnection) RemoteDescription() *SessionDescription { if pc.pendingRemoteDescription != nil { return pc.pendingRemoteDescription } + return pc.currentRemoteDescription } @@ -1806,8 +1930,10 @@ func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) error { if err != nil { if errors.Is(err, ice.ErrUnknownCandidateTyp) || errors.Is(err, ice.ErrDetermineNetworkType) { pc.log.Warnf("Discarding remote candidate: %s", err) + return nil } + return err } @@ -1827,10 +1953,11 @@ func (pc *PeerConnection) ICEConnectionState() ICEConnectionState { if state, ok := pc.iceConnectionState.Load().(ICEConnectionState); ok { return state } + return ICEConnectionState(0) } -// GetSenders returns the RTPSender that are currently attached to this PeerConnection +// GetSenders returns the RTPSender that are currently attached to this PeerConnection. func (pc *PeerConnection) GetSenders() (result []*RTPSender) { pc.mu.Lock() defer pc.mu.Unlock() @@ -1840,10 +1967,11 @@ func (pc *PeerConnection) GetSenders() (result []*RTPSender) { result = append(result, sender) } } + return result } -// GetReceivers returns the RTPReceivers that are currently attached to this PeerConnection +// GetReceivers returns the RTPReceivers that are currently attached to this PeerConnection. func (pc *PeerConnection) GetReceivers() (receivers []*RTPReceiver) { pc.mu.Lock() defer pc.mu.Unlock() @@ -1853,10 +1981,11 @@ func (pc *PeerConnection) GetReceivers() (receivers []*RTPReceiver) { receivers = append(receivers, receiver) } } + return } -// GetTransceivers returns the RtpTransceiver that are currently attached to this PeerConnection +// GetTransceivers returns the RtpTransceiver that are currently attached to this PeerConnection. func (pc *PeerConnection) GetTransceivers() []*RTPTransceiver { pc.mu.Lock() defer pc.mu.Unlock() @@ -1864,7 +1993,9 @@ func (pc *PeerConnection) GetTransceivers() []*RTPTransceiver { return pc.rtpTransceivers } -// AddTrack adds a Track to the PeerConnection +// AddTrack adds a Track to the PeerConnection. +// +//nolint:cyclop func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { if pc.isClosed.get() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} @@ -1872,26 +2003,27 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { pc.mu.Lock() defer pc.mu.Unlock() - for _, t := range pc.rtpTransceivers { - currentDirection := t.getCurrentDirection() + for _, transceiver := range pc.rtpTransceivers { + currentDirection := transceiver.getCurrentDirection() // According to https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-addtrack, if the // transceiver can be reused only if it's currentDirection never be sendrecv or sendonly. // But that will cause sdp inflate. So we only check currentDirection's current value, // that's worked for all browsers. - if t.kind == track.Kind() && t.Sender() == nil && + if transceiver.kind == track.Kind() && transceiver.Sender() == nil && !(currentDirection == RTPTransceiverDirectionSendrecv || currentDirection == RTPTransceiverDirectionSendonly) { sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport) if err == nil { - err = t.SetSender(sender, track) + err = transceiver.SetSender(sender, track) if err != nil { _ = sender.Stop() - t.setSender(nil) + transceiver.setSender(nil) } } if err != nil { return nil, err } pc.onNegotiationNeeded() + return sender, nil } } @@ -1901,10 +2033,11 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { return nil, err } pc.addRTPTransceiver(transceiver) + return transceiver.Sender(), nil } -// RemoveTrack removes a Track from the PeerConnection +// RemoveTrack removes a Track from the PeerConnection. func (pc *PeerConnection) RemoveTrack(sender *RTPSender) (err error) { if pc.isClosed.get() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} @@ -1916,6 +2049,7 @@ func (pc *PeerConnection) RemoveTrack(sender *RTPSender) (err error) { for _, t := range pc.rtpTransceivers { if t.Sender() == sender { transceiver = t + break } } @@ -1927,41 +2061,52 @@ func (pc *PeerConnection) RemoveTrack(sender *RTPSender) (err error) { pc.onNegotiationNeeded() } } + return } -func (pc *PeerConnection) newTransceiverFromTrack(direction RTPTransceiverDirection, track TrackLocal, init ...RTPTransceiverInit) (t *RTPTransceiver, err error) { +//nolint:cyclop +func (pc *PeerConnection) newTransceiverFromTrack( + direction RTPTransceiverDirection, + track TrackLocal, + init ...RTPTransceiverInit, +) (t *RTPTransceiver, err error) { var ( - r *RTPReceiver - s *RTPSender + receiver *RTPReceiver + sender *RTPSender ) switch direction { case RTPTransceiverDirectionSendrecv: - r, err = pc.api.NewRTPReceiver(track.Kind(), pc.dtlsTransport) + receiver, err = pc.api.NewRTPReceiver(track.Kind(), pc.dtlsTransport) if err != nil { - return + return t, err } - s, err = pc.api.NewRTPSender(track, pc.dtlsTransport) + sender, err = pc.api.NewRTPSender(track, pc.dtlsTransport) case RTPTransceiverDirectionSendonly: - s, err = pc.api.NewRTPSender(track, pc.dtlsTransport) + sender, err = pc.api.NewRTPSender(track, pc.dtlsTransport) default: err = errPeerConnAddTransceiverFromTrackSupport } if err != nil { - return + return t, err } // Allow RTPTransceiverInit to override SSRC - if s != nil && len(s.trackEncodings) == 1 && + if sender != nil && len(sender.trackEncodings) == 1 && len(init) == 1 && len(init[0].SendEncodings) == 1 && init[0].SendEncodings[0].SSRC != 0 { - s.trackEncodings[0].ssrc = init[0].SendEncodings[0].SSRC + sender.trackEncodings[0].ssrc = init[0].SendEncodings[0].SSRC } - return newRTPTransceiver(r, s, direction, track.Kind(), pc.api), nil + return newRTPTransceiver(receiver, sender, direction, track.Kind(), pc.api), nil } // AddTransceiverFromKind Create a new RtpTransceiver and adds it to the set of transceivers. -func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RTPTransceiverInit) (t *RTPTransceiver, err error) { +// +//nolint:cyclop +func (pc *PeerConnection) AddTransceiverFromKind( + kind RTPCodecType, + init ...RTPTransceiverInit, +) (t *RTPTransceiver, err error) { if pc.isClosed.get() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -1998,11 +2143,15 @@ func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RTPT pc.mu.Lock() pc.addRTPTransceiver(t) pc.mu.Unlock() + return t, nil } // AddTransceiverFromTrack Create a new RtpTransceiver(SendRecv or SendOnly) and add it to the set of transceivers. -func (pc *PeerConnection) AddTransceiverFromTrack(track TrackLocal, init ...RTPTransceiverInit) (t *RTPTransceiver, err error) { +func (pc *PeerConnection) AddTransceiverFromTrack( + track TrackLocal, + init ...RTPTransceiverInit, +) (t *RTPTransceiver, err error) { if pc.isClosed.get() { return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -2020,12 +2169,15 @@ func (pc *PeerConnection) AddTransceiverFromTrack(track TrackLocal, init ...RTPT pc.addRTPTransceiver(t) pc.mu.Unlock() } + return } // CreateDataChannel creates a new DataChannel object with the given label // and optional DataChannelInit used to configure properties of the // underlying channel such as data reliability. +// +//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() { @@ -2042,7 +2194,7 @@ func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelIn params.ID = options.ID } - if options != nil { + if options != nil { //nolint:nestif // Ordered indicates if data is allowed to be delivered out of order. The // default value of true, guarantees that data will be delivered in order. // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #9) @@ -2076,27 +2228,27 @@ func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelIn } } - d, err := pc.api.newDataChannel(params, nil, pc.log) + dataChannel, err := pc.api.newDataChannel(params, nil, pc.log) if err != nil { return nil, err } // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #16) - if d.maxPacketLifeTime != nil && d.maxRetransmits != nil { + if dataChannel.maxPacketLifeTime != nil && dataChannel.maxRetransmits != nil { return nil, &rtcerr.TypeError{Err: ErrRetransmitsOrPacketLifeTime} } pc.sctpTransport.lock.Lock() - pc.sctpTransport.dataChannels = append(pc.sctpTransport.dataChannels, d) - if d.ID() != nil { - pc.sctpTransport.dataChannelIDsUsed[*d.ID()] = struct{}{} + pc.sctpTransport.dataChannels = append(pc.sctpTransport.dataChannels, dataChannel) + if dataChannel.ID() != nil { + pc.sctpTransport.dataChannelIDsUsed[*dataChannel.ID()] = struct{}{} } pc.sctpTransport.dataChannelsRequested++ pc.sctpTransport.lock.Unlock() // If SCTP already connected open all the channels if pc.sctpTransport.State() == SCTPTransportStateConnected { - if err = d.open(pc.sctpTransport); err != nil { + if err = dataChannel.open(pc.sctpTransport); err != nil { return nil, err } } @@ -2105,10 +2257,10 @@ func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelIn pc.onNegotiationNeeded() pc.mu.Unlock() - return d, nil + return dataChannel, nil } -// SetIdentityProvider is used to configure an identity provider to generate identity assertions +// SetIdentityProvider is used to configure an identity provider to generate identity assertions. func (pc *PeerConnection) SetIdentityProvider(string) error { return errPeerConnSetIdentityProviderNotImplemented } @@ -2117,6 +2269,7 @@ func (pc *PeerConnection) SetIdentityProvider(string) error { // packet is discarded. It also runs any configured interceptors. func (pc *PeerConnection) WriteRTCP(pkts []rtcp.Packet) error { _, err := pc.interceptorRTCPWriter.Write(pkts, make(interceptor.Attributes)) + return err } @@ -2136,7 +2289,7 @@ func (pc *PeerConnection) GracefulClose() error { return pc.close(true /* shouldGracefullyClose */) } -func (pc *PeerConnection) close(shouldGracefullyClose bool) error { +func (pc *PeerConnection) close(shouldGracefullyClose bool) error { //nolint:cyclop // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1) // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2) @@ -2164,6 +2317,7 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error { // to happen and then return. if isAlreadyGracefullyClosingOrClosed { <-pc.isGracefulCloseDone + return nil } // Otherwise we need to go through the graceful closure flow once the @@ -2204,6 +2358,7 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error { gracefulCloseErrors = append(gracefulCloseErrors, d.GracefulClose()) } pc.sctpTransport.lock.Unlock() + return gracefulCloseErrors } @@ -2217,10 +2372,10 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4) pc.mu.Lock() for _, t := range pc.rtpTransceivers { - closeErrs = append(closeErrs, t.Stop()) + closeErrs = append(closeErrs, t.Stop()) //nolint:makezero // todo fix } if nonMediaBandwidthProbe, ok := pc.nonMediaBandwidthProbe.Load().(*RTPReceiver); ok { - closeErrs = append(closeErrs, nonMediaBandwidthProbe.Stop()) + closeErrs = append(closeErrs, nonMediaBandwidthProbe.Stop()) //nolint:makezero // todo fix } pc.mu.Unlock() @@ -2233,32 +2388,32 @@ func (pc *PeerConnection) close(shouldGracefullyClose bool) error { // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #6) if pc.sctpTransport != nil { - closeErrs = append(closeErrs, pc.sctpTransport.Stop()) + closeErrs = append(closeErrs, pc.sctpTransport.Stop()) //nolint:makezero // todo fix } // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #7) - closeErrs = append(closeErrs, pc.dtlsTransport.Stop()) + closeErrs = append(closeErrs, pc.dtlsTransport.Stop()) //nolint:makezero // todo fix // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10) if pc.iceTransport != nil && !shouldGracefullyClose { // we will stop gracefully in doGracefulCloseOps - closeErrs = append(closeErrs, pc.iceTransport.Stop()) + closeErrs = append(closeErrs, pc.iceTransport.Stop()) //nolint:makezero // todo fix } // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11) pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State()) - closeErrs = append(closeErrs, doGracefulCloseOps()...) + closeErrs = append(closeErrs, doGracefulCloseOps()...) //nolint:makezero // todo fix // Interceptor closes at the end to prevent Bind from being called after interceptor is closed - closeErrs = append(closeErrs, pc.api.interceptor.Close()) + closeErrs = append(closeErrs, pc.api.interceptor.Close()) //nolint:makezero // todo fix return util.FlattenErrs(closeErrs) } // addRTPTransceiver appends t into rtpTransceivers // and fires onNegotiationNeeded; -// caller of this method should hold `pc.mu` lock +// caller of this method should hold `pc.mu` lock. func (pc *PeerConnection) addRTPTransceiver(t *RTPTransceiver) { pc.rtpTransceivers = append(pc.rtpTransceivers, t) pc.onNegotiationNeeded() @@ -2275,6 +2430,7 @@ func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription { localDescription := pc.currentLocalDescription iceGather := pc.iceGatherer iceGatheringState := pc.ICEGatheringState() + return populateLocalCandidates(localDescription, iceGather, iceGatheringState) } @@ -2289,6 +2445,7 @@ func (pc *PeerConnection) PendingLocalDescription() *SessionDescription { localDescription := pc.pendingLocalDescription iceGather := pc.iceGatherer iceGatheringState := pc.ICEGatheringState() + return populateLocalCandidates(localDescription, iceGather, iceGatheringState) } @@ -2344,10 +2501,11 @@ func (pc *PeerConnection) ConnectionState() PeerConnectionState { if state, ok := pc.connectionState.Load().(PeerConnectionState); ok { return state } + return PeerConnectionState(0) } -// GetStats return data providing statistics about the overall connection +// GetStats return data providing statistics about the overall connection. func (pc *PeerConnection) GetStats() StatsReport { var ( dataChannelsAccepted uint32 @@ -2408,8 +2566,12 @@ func (pc *PeerConnection) GetStats() StatsReport { return statsCollector.Ready() } -// Start all transports. PeerConnection now has enough state -func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, remoteUfrag, remotePwd, fingerprint, fingerprintHash string) { +// Start all transports. PeerConnection now has enough state. +func (pc *PeerConnection) startTransports( + iceRole ICERole, + dtlsRole DTLSRole, + remoteUfrag, remotePwd, fingerprint, fingerprintHash string, +) { // Start the ice transport err := pc.iceTransport.Start( pc.iceGatherer, @@ -2422,6 +2584,7 @@ func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, re ) if err != nil { pc.log.Warnf("Failed to start manager: %s", err) + return } @@ -2446,12 +2609,17 @@ func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, re pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State()) if err != nil { pc.log.Warnf("Failed to start manager: %s", err) + return } } // nolint: gocognit -func (pc *PeerConnection) startRTP(isRenegotiation bool, remoteDesc *SessionDescription, currentTransceivers []*RTPTransceiver) { +func (pc *PeerConnection) startRTP( + isRenegotiation bool, + remoteDesc *SessionDescription, + currentTransceivers []*RTPTransceiver, +) { if !isRenegotiation { pc.undeclaredMediaProcessor() } @@ -2463,13 +2631,18 @@ func (pc *PeerConnection) startRTP(isRenegotiation bool, remoteDesc *SessionDesc } // generateUnmatchedSDP generates an SDP that doesn't take remote state into account -// This is used for the initial call for CreateOffer -func (pc *PeerConnection) generateUnmatchedSDP(transceivers []*RTPTransceiver, useIdentity bool) (*sdp.SessionDescription, error) { - d, err := sdp.NewJSEPSessionDescription(useIdentity) +// This is used for the initial call for CreateOffer. +// +//nolint:cyclop +func (pc *PeerConnection) generateUnmatchedSDP( + transceivers []*RTPTransceiver, + useIdentity bool, +) (*sdp.SessionDescription, error) { + desc, err := sdp.NewJSEPSessionDescription(useIdentity) if err != nil { return nil, err } - d.Attributes = append(d.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 { @@ -2488,7 +2661,7 @@ func (pc *PeerConnection) generateUnmatchedSDP(transceivers []*RTPTransceiver, u pc.sctpTransport.lock.Lock() defer pc.sctpTransport.lock.Unlock() - if isPlanB { + if isPlanB { //nolint:nestif video := make([]*RTPTransceiver, 0) audio := make([]*RTPTransceiver, 0) @@ -2531,18 +2704,37 @@ func (pc *PeerConnection) generateUnmatchedSDP(transceivers []*RTPTransceiver, u return nil, err } - return populateSDP(d, isPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, true, pc.api.mediaEngine, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), candidates, iceParams, mediaSections, pc.ICEGatheringState(), nil) + return populateSDP( + desc, + isPlanB, + dtlsFingerprints, + pc.api.settingEngine.sdpMediaLevelFingerprints, + pc.api.settingEngine.candidates.ICELite, + true, + pc.api.mediaEngine, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + candidates, + iceParams, + mediaSections, + pc.ICEGatheringState(), + nil, + ) } // generateMatchedSDP generates a SDP and takes the remote state into account // this is used everytime we have a RemoteDescription -// nolint: gocyclo -func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, useIdentity bool, includeUnmatched bool, connectionRole sdp.ConnectionRole) (*sdp.SessionDescription, error) { //nolint:gocognit - d, err := sdp.NewJSEPSessionDescription(useIdentity) +// +//nolint:gocognit,gocyclo,cyclop +func (pc *PeerConnection) generateMatchedSDP( + transceivers []*RTPTransceiver, + useIdentity, includeUnmatched bool, + connectionRole sdp.ConnectionRole, +) (*sdp.SessionDescription, error) { + desc, err := sdp.NewJSEPSessionDescription(useIdentity) if err != nil { return nil, err } - d.Attributes = append(d.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 { @@ -2554,7 +2746,7 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use return nil, err } - var t *RTPTransceiver + var transceiver *RTPTransceiver remoteDescription := pc.currentRemoteDescription if pc.pendingRemoteDescription != nil { remoteDescription = pc.pendingRemoteDescription @@ -2578,6 +2770,7 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use if media.MediaName.Media == mediaSectionApplication { mediaSections = append(mediaSections, mediaSection{id: midValue, data: true}) alreadyHaveApplicationMediaSection = true + continue } @@ -2592,49 +2785,60 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use switch { case sdpSemantics == SDPSemanticsPlanB || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback && detectedPlanB: if !detectedPlanB { - return nil, &rtcerr.TypeError{Err: fmt.Errorf("%w: Expected PlanB, but RemoteDescription is UnifiedPlan", ErrIncorrectSDPSemantics)} + return nil, &rtcerr.TypeError{ + Err: fmt.Errorf("%w: Expected PlanB, but RemoteDescription is UnifiedPlan", ErrIncorrectSDPSemantics), + } } // If we're responding to a plan-b offer, then we should try to fill up this // media entry with all matching local transceivers mediaTransceivers := []*RTPTransceiver{} for { // keep going until we can't get any more - t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers) - if t == nil { + transceiver, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers) + if transceiver == nil { if len(mediaTransceivers) == 0 { - t = &RTPTransceiver{kind: kind, api: pc.api, codecs: pc.api.mediaEngine.getCodecsByKind(kind)} - t.setDirection(RTPTransceiverDirectionInactive) - mediaTransceivers = append(mediaTransceivers, t) + transceiver = &RTPTransceiver{kind: kind, api: pc.api, codecs: pc.api.mediaEngine.getCodecsByKind(kind)} + transceiver.setDirection(RTPTransceiverDirectionInactive) + mediaTransceivers = append(mediaTransceivers, transceiver) } + break } - if sender := t.Sender(); sender != nil { + if sender := transceiver.Sender(); sender != nil { sender.setNegotiated() } - mediaTransceivers = append(mediaTransceivers, t) + mediaTransceivers = append(mediaTransceivers, transceiver) } mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers}) case sdpSemantics == SDPSemanticsUnifiedPlan || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback: if detectedPlanB { - return nil, &rtcerr.TypeError{Err: fmt.Errorf("%w: Expected UnifiedPlan, but RemoteDescription is PlanB", ErrIncorrectSDPSemantics)} + return nil, &rtcerr.TypeError{ + Err: fmt.Errorf( + "%w: Expected UnifiedPlan, but RemoteDescription is PlanB", + ErrIncorrectSDPSemantics, + ), + } } - t, localTransceivers = findByMid(midValue, localTransceivers) - if t == nil { + transceiver, localTransceivers = findByMid(midValue, localTransceivers) + if transceiver == nil { return nil, fmt.Errorf("%w: %q", errPeerConnTranscieverMidNil, midValue) } - if sender := t.Sender(); sender != nil { + if sender := transceiver.Sender(); sender != nil { sender.setNegotiated() } - mediaTransceivers := []*RTPTransceiver{t} + mediaTransceivers := []*RTPTransceiver{transceiver} extensions, _ := rtpExtensionsFromMediaDescription(media) - mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers, matchExtensions: extensions, rids: getRids(media)}) + mediaSections = append( + mediaSections, + mediaSection{id: midValue, transceivers: mediaTransceivers, matchExtensions: extensions, rids: getRids(media)}, + ) } } var bundleGroup *string // If we are offering also include unmatched local transceivers - if includeUnmatched { + if includeUnmatched { //nolint:nestif if !detectedPlanB { for _, t := range localTransceivers { if sender := t.Sender(); sender != nil { @@ -2666,7 +2870,21 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use return nil, err } - return populateSDP(d, detectedPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, isExtmapAllowMixed, pc.api.mediaEngine, connectionRole, candidates, iceParams, mediaSections, pc.ICEGatheringState(), bundleGroup) + return populateSDP( + desc, + detectedPlanB, + dtlsFingerprints, + pc.api.settingEngine.sdpMediaLevelFingerprints, + pc.api.settingEngine.candidates.ICELite, + isExtmapAllowMixed, + pc.api.mediaEngine, + connectionRole, + candidates, + iceParams, + mediaSections, + pc.ICEGatheringState(), + bundleGroup, + ) } func (pc *PeerConnection) setGatherCompleteHandler(handler func()) { diff --git a/peerconnection_close_test.go b/peerconnection_close_test.go index 1bdbb0ad..f389af2c 100644 --- a/peerconnection_close_test.go +++ b/peerconnection_close_test.go @@ -63,7 +63,7 @@ func TestPeerConnection_Close(t *testing.T) { <-awaitICEClosed } -// Assert that a PeerConnection that is shutdown before ICE starts doesn't leak +// Assert that a PeerConnection that is shutdown before ICE starts doesn't leak. func TestPeerConnection_Close_PreICE(t *testing.T) { // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 30) @@ -111,7 +111,7 @@ func TestPeerConnection_Close_PreICE(t *testing.T) { } } -func TestPeerConnection_Close_DuringICE(t *testing.T) { +func TestPeerConnection_Close_DuringICE(t *testing.T) { //nolint:cyclop // Limit runtime in case of deadlocks lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/peerconnection_go_test.go b/peerconnection_go_test.go index c7d15bc7..86106b3a 100644 --- a/peerconnection_go_test.go +++ b/peerconnection_go_test.go @@ -340,7 +340,7 @@ func TestPeerConnection_EventHandlers_Go(t *testing.T) { } // This test asserts that nothing deadlocks we try to shutdown when DTLS is in flight -// We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost +// We ensure that DTLS is in flight by removing the mux func for it, so all inbound DTLS is lost. func TestPeerConnection_ShutdownNoDTLS(t *testing.T) { lim := test.TimeOut(time.Second * 10) defer lim.Stop() @@ -477,7 +477,12 @@ func TestPeerConnection_satisfyTypeAndDirection(t *testing.T) { { "No local Transceivers, every remote should get nil", []RTPCodecType{RTPCodecTypeVideo, RTPCodecTypeAudio, RTPCodecTypeVideo, RTPCodecTypeVideo}, - []RTPTransceiverDirection{RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly, RTPTransceiverDirectionInactive}, + []RTPTransceiverDirection{ + RTPTransceiverDirectionSendrecv, + RTPTransceiverDirectionRecvonly, + RTPTransceiverDirectionSendonly, + RTPTransceiverDirectionInactive, + }, []*RTPTransceiver{}, @@ -658,15 +663,19 @@ func TestOnICEGatheringStateChange(t *testing.T) { assert.NoError(t, peerConn.Close()) } -// Assert Trickle ICE behaviors -func TestPeerConnectionTrickle(t *testing.T) { +// Assert Trickle ICE behaviors. +func TestPeerConnectionTrickle(t *testing.T) { //nolint:cyclop offerPC, answerPC, err := newPair() assert.NoError(t, err) _, err = offerPC.CreateDataChannel("test-channel", nil) assert.NoError(t, err) - addOrCacheCandidate := func(pc *PeerConnection, c *ICECandidate, candidateCache []ICECandidateInit) []ICECandidateInit { + addOrCacheCandidate := func( + pc *PeerConnection, + c *ICECandidate, + candidateCache []ICECandidateInit, + ) []ICECandidateInit { if c == nil { return candidateCache } @@ -676,6 +685,7 @@ func TestPeerConnectionTrickle(t *testing.T) { } assert.NoError(t, pc.AddICECandidate(c.ToJSON())) + return candidateCache } @@ -752,7 +762,7 @@ func TestPeerConnectionTrickle(t *testing.T) { closePairNow(t, offerPC, answerPC) } -// Issue #1121, assert populateLocalCandidates doesn't mutate +// Issue #1121, assert populateLocalCandidates doesn't mutate. func TestPopulateLocalCandidates(t *testing.T) { t.Run("PendingLocalDescription shouldn't add extra mutations", func(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) @@ -792,7 +802,7 @@ func TestPopulateLocalCandidates(t *testing.T) { }) } -// Assert that two agents that only generate mDNS candidates can connect +// Assert that two agents that only generate mDNS candidates can connect. func TestMulticastDNSCandidates(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -896,7 +906,7 @@ func TestICERestart(t *testing.T) { closePairNow(t, offerPC, answerPC) } -// Assert error handling when an Agent is restart +// Assert error handling when an Agent is restart. func TestICERestart_Error_Handling(t *testing.T) { iceStates := make(chan ICEConnectionState, 100) blockUntilICEState := func(wantedState ICEConnectionState) { @@ -957,9 +967,9 @@ func TestICERestart_Error_Handling(t *testing.T) { }) dataChannelAnswerer := make(chan *DataChannel) - offerPeerConnection.OnDataChannel(func(d *DataChannel) { - d.OnOpen(func() { - dataChannelAnswerer <- d + offerPeerConnection.OnDataChannel(func(dataChannel *DataChannel) { + dataChannel.OnOpen(func() { + dataChannelAnswerer <- dataChannel }) }) @@ -1010,6 +1020,7 @@ func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) { trackID := fmt.Sprintf("pion-track-%d", len(r.trackIDs)) track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, "pion") r.trackIDs[trackID] = struct{}{} + return track, err } @@ -1024,12 +1035,14 @@ func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) { func (r *trackRecords) remains() int { r.mu.Lock() + defer r.mu.Unlock() + return len(r.trackIDs) - len(r.receivedTrackIDs) } // This test assure that all track events emits. -func TestPeerConnection_MassiveTracks(t *testing.T) { +func TestPeerConnection_MassiveTracks(t *testing.T) { //nolint:cyclop var ( tRecs = &trackRecords{ trackIDs: make(map[string]struct{}), @@ -1166,7 +1179,7 @@ a=mid:data ` // this test asserts that if an ice-lite offer is received, -// pion will take the ICE-CONTROLLING role +// pion will take the ICE-CONTROLLING role. func TestICELite(t *testing.T) { peerConnection, err := NewPeerConnection(Configuration{}) assert.NoError(t, err) @@ -1206,6 +1219,7 @@ func TestPeerConnection_TransceiverDirection(t *testing.T) { _, err = pc.AddTransceiverFromTrack(track, []RTPTransceiverInit{ {Direction: dir}, }...) + return err } @@ -1213,6 +1227,7 @@ func TestPeerConnection_TransceiverDirection(t *testing.T) { RTPCodecTypeVideo, RTPTransceiverInit{Direction: dir}, ) + return err } @@ -1424,17 +1439,19 @@ a=sendonly a=rtpmap:125 H264/90000 a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 94, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, - PayloadType: 98, + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil, + }, + PayloadType: 98, }, RTPCodecTypeVideo)) - api := NewAPI(WithMediaEngine(&m)) + api := NewAPI(WithMediaEngine(&mediaEngine)) pc, err := api.NewPeerConnection(Configuration{}) assert.NoError(t, err) assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ @@ -1483,17 +1500,19 @@ a=rtpmap:98 H264/90000 a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=sendonly ` - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 94, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil}, - PayloadType: 98, + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", nil, + }, + PayloadType: 98, }, RTPCodecTypeVideo)) - api := NewAPI(WithMediaEngine(&m)) + api := NewAPI(WithMediaEngine(&mediaEngine)) pc, err := api.NewPeerConnection(Configuration{}) assert.NoError(t, err) assert.NoError(t, pc.SetRemoteDescription(SessionDescription{ @@ -1567,7 +1586,10 @@ a=ssrc:1455629982 cname:{61fd3093-0326-4b12-8258-86bdc1fe677a} assert.NoError(t, err) assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: remoteSDP})) - assert.Equal(t, RTPTransceiverDirectionInactive, peerConnection.rtpTransceivers[0].direction.Load().(RTPTransceiverDirection)) //nolint:forcetypeassert + assert.Equal( + t, RTPTransceiverDirectionInactive, + peerConnection.rtpTransceivers[0].direction.Load().(RTPTransceiverDirection), //nolint:forcetypeassert + ) assert.NoError(t, peerConnection.Close()) } @@ -1643,7 +1665,7 @@ func TestPeerConnectionDeadlock(t *testing.T) { } // Assert that by default NULL Ciphers aren't enabled. Even if -// the remote Peer Requests a NULL Cipher we should fail +// the remote Peer Requests a NULL Cipher we should fail. func TestPeerConnectionNoNULLCipherDefault(t *testing.T) { settingEngine := SettingEngine{} settingEngine.SetSRTPProtectionProfiles(dtls.SRTP_NULL_HMAC_SHA1_80, dtls.SRTP_NULL_HMAC_SHA1_32) @@ -1724,12 +1746,16 @@ a=ssrc:29586368 cname:janus mediaEngine := &MediaEngine{} assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo)) assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 111, + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 111, }, RTPCodecTypeAudio)) api := NewAPI(WithMediaEngine(mediaEngine)) @@ -1776,12 +1802,16 @@ func TestTranceiverMediaStreamIdentification(t *testing.T) { mediaEngine := &MediaEngine{} assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo)) assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 111, + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 111, }, RTPCodecTypeAudio)) api := NewAPI(WithMediaEngine(mediaEngine)) diff --git a/peerconnection_media_test.go b/peerconnection_media_test.go index 23259bfb..30c9307a 100644 --- a/peerconnection_media_test.go +++ b/peerconnection_media_test.go @@ -45,7 +45,7 @@ Integration test for bi-directional peers This asserts we can send RTP and RTCP both ways, and blocks until each side gets something (and asserts payload contents) */ -// nolint: gocyclo +//nolint:gocyclo,cyclop func TestPeerConnection_Media_Sample(t *testing.T) { const ( expectedTrackID = "video" @@ -77,12 +77,18 @@ func TestPeerConnection_Media_Sample(t *testing.T) { pcAnswer.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) { if track.ID() != expectedTrackID { - trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackIDInvalid, expectedTrackID, track.ID()) + trackMetadataValid <- fmt.Errorf( + "%w: expected(%s) actual(%s)", errIncomingTrackIDInvalid, expectedTrackID, track.ID(), + ) + return } if track.StreamID() != expectedStreamID { - trackMetadataValid <- fmt.Errorf("%w: expected(%s) actual(%s)", errIncomingTrackLabelInvalid, expectedStreamID, track.StreamID()) + trackMetadataValid <- fmt.Errorf( + "%w: expected(%s) actual(%s)", errIncomingTrackLabelInvalid, expectedStreamID, track.StreamID(), + ) + return } close(trackMetadataValid) @@ -90,14 +96,18 @@ func TestPeerConnection_Media_Sample(t *testing.T) { go func() { for { time.Sleep(time.Millisecond * 100) - if routineErr := pcAnswer.WriteRTCP([]rtcp.Packet{&rtcp.RapidResynchronizationRequest{SenderSSRC: uint32(track.SSRC()), MediaSSRC: uint32(track.SSRC())}}); routineErr != nil { + if routineErr := pcAnswer.WriteRTCP([]rtcp.Packet{&rtcp.RapidResynchronizationRequest{ + SenderSSRC: uint32(track.SSRC()), MediaSSRC: uint32(track.SSRC()), + }}); routineErr != nil { awaitRTCPReceiverSend <- routineErr + return } select { case <-awaitRTCPSenderRecv: close(awaitRTCPReceiverSend) + return default: } @@ -118,6 +128,7 @@ func TestPeerConnection_Media_Sample(t *testing.T) { p, _, routineErr := track.ReadRTP() if routineErr != nil { close(awaitRTPRecvClosed) + return } else if bytes.Equal(p.Payload, []byte{0x10, 0x00}) && !haveClosedAwaitRTPRecv { haveClosedAwaitRTPRecv = true @@ -126,7 +137,9 @@ func TestPeerConnection_Media_Sample(t *testing.T) { } }) - vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, expectedTrackID, expectedStreamID) + vp8Track, err := NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, expectedTrackID, expectedStreamID, + ) if err != nil { t.Fatal(err) } @@ -148,6 +161,7 @@ func TestPeerConnection_Media_Sample(t *testing.T) { select { case <-awaitRTPRecv: close(awaitRTPSend) + return default: } @@ -159,13 +173,18 @@ func TestPeerConnection_Media_Sample(t *testing.T) { for { time.Sleep(time.Millisecond * 100) - if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{SenderSSRC: uint32(parameters.Encodings[0].SSRC), MediaSSRC: uint32(parameters.Encodings[0].SSRC)}}); routineErr != nil { + if routineErr := pcOffer.WriteRTCP([]rtcp.Packet{ + &rtcp.PictureLossIndication{ + SenderSSRC: uint32(parameters.Encodings[0].SSRC), MediaSSRC: uint32(parameters.Encodings[0].SSRC), + }, + }); routineErr != nil { awaitRTCPSenderSend <- routineErr } select { case <-awaitRTCPReceiverRecv: close(awaitRTCPSenderSend) + return default: } @@ -202,15 +221,12 @@ func TestPeerConnection_Media_Sample(t *testing.T) { <-awaitRTPRecvClosed } -/* -PeerConnection should be able to be torn down at anytime -This test adds an input track and asserts - -* OnTrack doesn't fire since no video packets will arrive -* No goroutine leaks -* No deadlocks on shutdown -*/ -func TestPeerConnection_Media_Shutdown(t *testing.T) { +// PeerConnection should be able to be torn down at anytime +// This test adds an input track and asserts +// OnTrack doesn't fire since no video packets will arrive +// No goroutine leaks +// No deadlocks on shutdown. +func TestPeerConnection_Media_Shutdown(t *testing.T) { //nolint:cyclop iceCompleteAnswer := make(chan struct{}) iceCompleteOffer := make(chan struct{}) @@ -225,12 +241,18 @@ func TestPeerConnection_Media_Shutdown(t *testing.T) { t.Fatal(err) } - _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcOffer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) if err != nil { t.Fatal(err) } - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeAudio, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) if err != nil { t.Fatal(err) } @@ -305,12 +327,10 @@ func TestPeerConnection_Media_Shutdown(t *testing.T) { onTrackFiredLock.Unlock() } -/* -Integration test for behavior around media and disconnected peers +// Integration test for behavior around media and disconnected peers +// Sending RTP and RTCP to a disconnected Peer shouldn't return an error. -* Sending RTP and RTCP to a disconnected Peer shouldn't return an error -*/ -func TestPeerConnection_Media_Disconnected(t *testing.T) { +func TestPeerConnection_Media_Disconnected(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -320,8 +340,8 @@ func TestPeerConnection_Media_Disconnected(t *testing.T) { s := SettingEngine{} s.SetICETimeouts(time.Second/2, time.Second/2, time.Second/8) - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) pcOffer, pcAnswer, wan := createVNetPair(t, nil) @@ -406,7 +426,7 @@ func (u *undeclaredSsrcLoggerFactory) NewLogger(string) logging.LeveledLogger { return &undeclaredSsrcLogger{u.unhandledSimulcastError} } -// Filter SSRC lines +// Filter SSRC lines. func filterSsrc(offer string) (filteredSDP string) { scanner := bufio.NewScanner(strings.NewReader(offer)) for scanner.Scan() { @@ -417,11 +437,12 @@ func filterSsrc(offer string) (filteredSDP string) { filteredSDP += l + "\n" } + return } // If a SessionDescription has a single media section and no SSRC -// assume that it is meant to handle all RTP packets +// assume that it is meant to handle all RTP packets. func TestUndeclaredSSRC(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -465,19 +486,19 @@ func TestUndeclaredSSRC(t *testing.T) { assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) - sendVideoUntilDone(onTrackFired, t, []*TrackLocalStaticSample{vp8Writer}) + sendVideoUntilDone(t, onTrackFired, []*TrackLocalStaticSample{vp8Writer}) closePairNow(t, pcOffer, pcAnswer) }) t.Run("Has RID", func(t *testing.T) { unhandledSimulcastError := make(chan struct{}) - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{ LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError}, - }), WithMediaEngine(m)).newPair(Configuration{}) + }), WithMediaEngine(mediaEngine)).newPair(Configuration{}) assert.NoError(t, err) vp8Writer, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2") @@ -506,7 +527,7 @@ func TestUndeclaredSSRC(t *testing.T) { assert.NoError(t, pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription())) - sendVideoUntilDone(unhandledSimulcastError, t, []*TrackLocalStaticSample{vp8Writer}) + sendVideoUntilDone(t, unhandledSimulcastError, []*TrackLocalStaticSample{vp8Writer}) closePairNow(t, pcOffer, pcAnswer) }) } @@ -755,7 +776,10 @@ func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) { } track, err := NewTrackLocalStaticSample( - RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"}, + RTPCodecCapability{ + MimeType: MimeTypeH264, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, "track-id", "track-label", ) @@ -776,9 +800,15 @@ func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) { } func TestPlanBMediaExchange(t *testing.T) { - runTest := func(trackCount int, t *testing.T) { + runTest := func(t *testing.T, trackCount int) { + t.Helper() + addSingleTrack := func(p *PeerConnection) *TrackLocalStaticSample { - track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, fmt.Sprintf("video-%d", util.RandUint32()), fmt.Sprintf("video-%d", util.RandUint32())) + track, err := NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, + fmt.Sprintf("video-%d", util.RandUint32()), + fmt.Sprintf("video-%d", util.RandUint32()), + ) assert.NoError(t, err) _, err = p.AddTrack(track) @@ -838,16 +868,16 @@ func TestPlanBMediaExchange(t *testing.T) { defer report() t.Run("Single Track", func(t *testing.T) { - runTest(1, t) + runTest(t, 1) }) t.Run("Multi Track", func(t *testing.T) { - runTest(2, t) + runTest(t, 2) }) } // TestPeerConnection_Start_Only_Negotiated_Senders tests that only // the current negotiated transceivers senders provided in an -// offer/answer are started +// offer/answer are started. func TestPeerConnection_Start_Only_Negotiated_Senders(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -903,15 +933,17 @@ func TestPeerConnection_Start_Only_Negotiated_Senders(t *testing.T) { // TestPeerConnection_Start_Right_Receiver tests that the right // receiver (the receiver which transceiver has the same media section as the track) -// is started for the specified track +// is started for the specified track. func TestPeerConnection_Start_Right_Receiver(t *testing.T) { isTransceiverReceiverStarted := func(pc *PeerConnection, mid string) (bool, error) { for _, transceiver := range pc.GetTransceivers() { if transceiver.Mid() != mid { continue } + return transceiver.Receiver() != nil && transceiver.Receiver().haveReceived(), nil } + return false, fmt.Errorf("%w: %q", errNoTransceiverwithMid, mid) } @@ -924,7 +956,10 @@ func TestPeerConnection_Start_Right_Receiver(t *testing.T) { pcOffer, pcAnswer, err := newPair() require.NoError(t, err) - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) track1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1") @@ -960,7 +995,10 @@ func TestPeerConnection_Start_Right_Receiver(t *testing.T) { _, err = pcOffer.AddTransceiverFromTrack(track1) assert.NoError(t, err) - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) assert.NoError(t, signalPair(pcOffer, pcAnswer)) @@ -980,7 +1018,7 @@ func TestPeerConnection_Start_Right_Receiver(t *testing.T) { closePairNow(t, pcOffer, pcAnswer) } -func TestPeerConnection_Simulcast_Probe(t *testing.T) { +func TestPeerConnection_Simulcast_Probe(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 30) //nolint defer lim.Stop() @@ -1044,13 +1082,13 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { t.Run("Break NonSimulcast", func(t *testing.T) { unhandledSimulcastError := make(chan struct{}) - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) - assert.NoError(t, ConfigureSimulcastExtensionHeaders(m)) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) + assert.NoError(t, ConfigureSimulcastExtensionHeaders(mediaEngine)) pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(SettingEngine{ LoggerFactory: &undeclaredSsrcLoggerFactory{unhandledSimulcastError}, - }), WithMediaEngine(m)).newPair(Configuration{}) + }), WithMediaEngine(mediaEngine)).newPair(Configuration{}) assert.NoError(t, err) firstTrack, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "firstTrack", "firstTrack") @@ -1074,6 +1112,7 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { shouldDiscard = !shouldDiscard } else if strings.HasPrefix(scanner.Text(), "a=group:BUNDLE") { filtered += "a=group:BUNDLE 1 2\r\n" + continue } @@ -1081,6 +1120,7 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { filtered += scanner.Text() + "\r\n" } } + return })) @@ -1142,6 +1182,7 @@ func TestPeerConnection_Simulcast_Probe(t *testing.T) { // Assert that CreateOffer returns an error for a RTPSender with no codecs // pion/webrtc#1702 +// . func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1149,9 +1190,9 @@ func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) { report := test.CheckRoutines(t) defer report() - m := &MediaEngine{} + mediaEngine := &MediaEngine{} - pc, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + pc, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) track, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") @@ -1166,7 +1207,7 @@ func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) { assert.NoError(t, pc.Close()) } -// Assert that AddTrack is thread-safe +// Assert that AddTrack is thread-safe. func TestPeerConnection_RaceReplaceTrack(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) assert.NoError(t, err) @@ -1176,6 +1217,7 @@ func TestPeerConnection_RaceReplaceTrack(t *testing.T) { assert.NoError(t, err) _, err = pc.AddTrack(track) assert.NoError(t, err) + return track } @@ -1203,6 +1245,7 @@ func TestPeerConnection_RaceReplaceTrack(t *testing.T) { for _, t := range pc.GetTransceivers() { if t.Sender() != nil && t.Sender().Track() == track { have = true + break } } @@ -1214,7 +1257,7 @@ func TestPeerConnection_RaceReplaceTrack(t *testing.T) { assert.NoError(t, pc.Close()) } -func TestPeerConnection_Simulcast(t *testing.T) { +func TestPeerConnection_Simulcast(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1227,13 +1270,19 @@ func TestPeerConnection_Simulcast(t *testing.T) { pcOffer, pcAnswer, err := newPair() assert.NoError(t, err) - vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0])) + vp8WriterA, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]), + ) assert.NoError(t, err) - vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1])) + vp8WriterB, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]), + ) assert.NoError(t, err) - vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2])) + vp8WriterC, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2]), + ) assert.NoError(t, err) sender, err := pcOffer.AddTrack(vp8WriterA) @@ -1247,6 +1296,8 @@ func TestPeerConnection_Simulcast(t *testing.T) { ridMap := map[string]int{} assertRidCorrect := func(t *testing.T) { + t.Helper() + ridMapLock.Lock() defer ridMapLock.Unlock() @@ -1261,6 +1312,7 @@ func TestPeerConnection_Simulcast(t *testing.T) { defer ridMapLock.Unlock() ridCount := len(ridMap) + return ridCount == 3 } @@ -1279,9 +1331,9 @@ func TestPeerConnection_Simulcast(t *testing.T) { for _, extension := range parameters.HeaderExtensions { switch extension.URI { case sdp.SDESMidURI: - midID = uint8(extension.ID) + midID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRTPStreamIDURI: - ridID = uint8(extension.ID) + ridID = uint8(extension.ID) //nolint:gosec // G115 } } assert.NotZero(t, midID) @@ -1337,13 +1389,19 @@ func TestPeerConnection_Simulcast(t *testing.T) { pcOffer, pcAnswer, err := newPair() assert.NoError(t, err) - vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0])) + vp8WriterA, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]), + ) assert.NoError(t, err) - vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1])) + vp8WriterB, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]), + ) assert.NoError(t, err) - vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2])) + vp8WriterC, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[2]), + ) assert.NoError(t, err) sender, err := pcOffer.AddTrack(vp8WriterA) @@ -1364,9 +1422,9 @@ func TestPeerConnection_Simulcast(t *testing.T) { for _, extension := range sender.GetParameters().HeaderExtensions { switch extension.URI { case sdp.SDESMidURI: - midID = uint8(extension.ID) + midID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRTPStreamIDURI: - ridID = uint8(extension.ID) + ridID = uint8(extension.ID) //nolint:gosec // G115 } } assert.NotZero(t, midID) @@ -1423,7 +1481,7 @@ func (s *simulcastTestTrackLocal) WriteRTP(pkt *rtp.Packet) error { return util.FlattenErrs(writeErrs) } -func TestPeerConnection_Simulcast_RTX(t *testing.T) { +func TestPeerConnection_Simulcast_RTX(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1434,10 +1492,14 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { pcOffer, pcAnswer, err := newPair() assert.NoError(t, err) - vp8WriterAStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0])) + vp8WriterAStatic, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[0]), + ) assert.NoError(t, err) - vp8WriterBStatic, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1])) + vp8WriterBStatic, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion2", WithRTPStreamID(rids[1]), + ) assert.NoError(t, err) vp8WriterA, vp8WriterB := &simulcastTestTrackLocal{vp8WriterAStatic}, &simulcastTestTrackLocal{vp8WriterBStatic} @@ -1452,6 +1514,8 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { ridMap := map[string]int{} assertRidCorrect := func(t *testing.T) { + t.Helper() + ridMapLock.Lock() defer ridMapLock.Unlock() @@ -1466,6 +1530,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { defer ridMapLock.Unlock() ridCount := len(ridMap) + return ridCount == 2 } @@ -1501,11 +1566,11 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { for _, extension := range parameters.HeaderExtensions { switch extension.URI { case sdp.SDESMidURI: - midID = uint8(extension.ID) + midID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRTPStreamIDURI: - ridID = uint8(extension.ID) + ridID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRepairRTPStreamIDURI: - rsid = uint8(extension.ID) + rsid = uint8(extension.ID) //nolint:gosec // G115 } } assert.NotZero(t, midID) @@ -1516,6 +1581,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { // Original chrome sdp contains no ssrc info https://pastebin.com/raw/JTjX6zg6 re := regexp.MustCompile("(?m)[\r\n]+^.*a=ssrc.*$") res := re.ReplaceAllString(sdp, "") + return res }) assert.NoError(t, err) @@ -1532,7 +1598,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { SequenceNumber: sequenceNumber, PayloadType: 96, Padding: true, - SSRC: uint32(i + 1), + SSRC: uint32(i + 1), //nolint:gosec // G115 }, Payload: []byte{0x00, 0x02}, } @@ -1551,7 +1617,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { Version: 2, SequenceNumber: sequenceNumber, PayloadType: 96, - SSRC: uint32(i + 1), + SSRC: uint32(i + 1), //nolint:gosec // G115 }, Payload: []byte{0x00}, } @@ -1574,7 +1640,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { Version: 2, SequenceNumber: sequenceNumber, PayloadType: 97, - SSRC: uint32(100 + j), + SSRC: uint32(100 + j), //nolint:gosec // G115 }, Payload: []byte{0x00, 0x00, 0x00, 0x00, 0x00}, } @@ -1595,7 +1661,7 @@ func TestPeerConnection_Simulcast_RTX(t *testing.T) { Version: 2, SequenceNumber: sequenceNumber, PayloadType: 96, - SSRC: uint32(i + 1), + SSRC: uint32(i + 1), //nolint:gosec // G115 }, Payload: []byte{0x00}, } @@ -1653,19 +1719,25 @@ func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) { go func() { defer wg.Done() - vp8WriterA, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("a")) + vp8WriterA, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("a"), + ) assert.NoError(t, err) sender, err := pcSender.AddTrack(vp8WriterA) assert.NoError(t, err) assert.NotNil(t, sender) - vp8WriterB, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("b")) + vp8WriterB, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("b"), + ) assert.NoError(t, err) err = sender.AddEncoding(vp8WriterB) assert.NoError(t, err) - vp8WriterC, err := NewTrackLocalStaticRTP(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("c")) + vp8WriterC, err := NewTrackLocalStaticRTP( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("c"), + ) assert.NoError(t, err) err = sender.AddEncoding(vp8WriterC) assert.NoError(t, err) @@ -1675,11 +1747,11 @@ func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) { for _, extension := range parameters.HeaderExtensions { switch extension.URI { case sdp.SDESMidURI: - midID = uint8(extension.ID) + midID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRTPStreamIDURI: - ridID = uint8(extension.ID) + ridID = uint8(extension.ID) //nolint:gosec // G115 case sdp.SDESRepairRTPStreamIDURI: - rsidID = uint8(extension.ID) + rsidID = uint8(extension.ID) //nolint:gosec // G115 } } assert.NotZero(t, midID) @@ -1740,7 +1812,7 @@ func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) { // Check that PayloadType of 0 is handled correctly. At one point // we incorrectly assumed 0 meant an invalid stream and wouldn't update things -// properly +// properly. func TestPeerConnection_Zero_PayloadType(t *testing.T) { lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -1775,7 +1847,9 @@ func TestPeerConnection_Zero_PayloadType(t *testing.T) { case <-trackFired: return case <-ticker.C: - if routineErr := audioTrack.WriteSample(media.Sample{Data: []byte{0x00}, Duration: time.Second}); routineErr != nil { + if routineErr := audioTrack.WriteSample( + media.Sample{Data: []byte{0x00}, Duration: time.Second}, + ); routineErr != nil { fmt.Println(routineErr) } } @@ -1786,8 +1860,8 @@ func TestPeerConnection_Zero_PayloadType(t *testing.T) { } // Assert that NACKs work E2E with no extra configuration. If media is sent over a lossy connection -// the user gets retransmitted RTP packets with no extra configuration -func Test_PeerConnection_RTX_E2E(t *testing.T) { +// the user gets retransmitted RTP packets with no extra configuration. +func Test_PeerConnection_RTX_E2E(t *testing.T) { //nolint:cyclop defer test.TimeOut(time.Second * 30).Stop() pcOffer, pcAnswer, wan := createVNetPair(t, nil) diff --git a/peerconnection_renegotiation_test.go b/peerconnection_renegotiation_test.go index 8d2e5b7b..2782e953 100644 --- a/peerconnection_renegotiation_test.go +++ b/peerconnection_renegotiation_test.go @@ -27,7 +27,9 @@ import ( "github.com/stretchr/testify/require" ) -func sendVideoUntilDone(done <-chan struct{}, t *testing.T, tracks []*TrackLocalStaticSample) { +func sendVideoUntilDone(t *testing.T, done <-chan struct{}, tracks []*TrackLocalStaticSample) { + t.Helper() + for { select { case <-time.After(20 * time.Millisecond): @@ -64,6 +66,7 @@ func sdpMidHasSsrc(offer SessionDescription, mid string, ssrc SSRC) bool { return true } } + return false } @@ -139,19 +142,18 @@ func TestPeerConnection_Renegotiation_AddRecvonlyTransceiver(t *testing.T) { assert.NoError(t, signalPair(pcOffer, pcAnswer)) } - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{localTrack}) closePairNow(t, pcOffer, pcAnswer) }) } } -/* -* Assert the following behaviors -* - We are able to call AddTrack after signaling -* - OnTrack is NOT called on the other side until after SetRemoteDescription -* - We are able to re-negotiate and AddTrack is properly called - */ +// Assert the following behaviors +// +// - We are able to call AddTrack after signaling +// - OnTrack is NOT called on the other side until after SetRemoteDescription +// - We are able to re-negotiate and AddTrack is properly called. func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -175,7 +177,10 @@ func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) { assert.NoError(t, signalPair(pcOffer, pcAnswer)) - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") @@ -210,15 +215,18 @@ func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) { pcOffer.ops.Done() assert.Equal(t, 1, len(vp8Track.rtpTrack.bindings)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{vp8Track}) closePairNow(t, pcOffer, pcAnswer) } -// Assert that adding tracks across multiple renegotiations performs as expected +// Assert that adding tracks across multiple renegotiations performs as expected. func TestPeerConnection_Renegotiation_AddTrack_Multiple(t *testing.T) { addTrackWithLabel := func(trackID string, pcOffer, pcAnswer *PeerConnection) *TrackLocalStaticSample { - _, err := pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err := pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, trackID, trackID) @@ -256,7 +264,7 @@ func TestPeerConnection_Renegotiation_AddTrack_Multiple(t *testing.T) { for i := range trackIDs { outboundTracks = append(outboundTracks, addTrackWithLabel(trackIDs[i], pcOffer, pcAnswer)) assert.NoError(t, signalPair(pcOffer, pcAnswer)) - sendVideoUntilDone(onTrackChan, t, outboundTracks) + sendVideoUntilDone(t, onTrackChan, outboundTracks) } closePairNow(t, pcOffer, pcAnswer) @@ -296,7 +304,10 @@ func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) { atomicRemoteTrack.Store(track) }) - _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcOffer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo1", "bar1") assert.NoError(t, err) @@ -311,7 +322,7 @@ func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) { haveRenegotiated.set(true) assert.NoError(t, signalPair(pcOffer, pcAnswer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{vp8Track}) closePairNow(t, pcOffer, pcAnswer) @@ -323,7 +334,7 @@ func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) { } // TestPeerConnection_Transceiver_Mid tests that we'll provide the same -// transceiver for a media id on successive offer/answer +// transceiver for a media id on successive offer/answer. func TestPeerConnection_Transceiver_Mid(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -375,7 +386,14 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) { // Must have 3 media descriptions (2 video channels) assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) - assert.True(t, sdpMidHasSsrc(offer, "0", sender1.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "0", sender1.trackEncodings[0].ssrc, offer.SDP) + assert.True( + t, + sdpMidHasSsrc(offer, "0", sender1.trackEncodings[0].ssrc), + "Expected mid %q with ssrc %d, offer.SDP: %s", + "0", + sender1.trackEncodings[0].ssrc, + offer.SDP, + ) // Remove first track, must keep same number of media // descriptions and same track ssrc for mid 1 as previous @@ -387,7 +405,14 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) { assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) - assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.SDP: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP) + assert.True( + t, + sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), + "Expected mid %q with ssrc %d, offer.SDP: %s", + "1", + sender2.trackEncodings[0].ssrc, + offer.SDP, + ) _, err = pcAnswer.CreateAnswer(nil) assert.Equal(t, err, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState}) @@ -412,8 +437,22 @@ func TestPeerConnection_Transceiver_Mid(t *testing.T) { // We reuse the existing non-sending transceiver assert.Equal(t, len(offer.parsed.MediaDescriptions), 2) - assert.True(t, sdpMidHasSsrc(offer, "0", sender3.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "0", sender3.trackEncodings[0].ssrc, offer.SDP) - assert.True(t, sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), "Expected mid %q with ssrc %d, offer.sdp: %s", "1", sender2.trackEncodings[0].ssrc, offer.SDP) + assert.True( + t, + sdpMidHasSsrc(offer, "0", sender3.trackEncodings[0].ssrc), + "Expected mid %q with ssrc %d, offer.sdp: %s", + "0", + sender3.trackEncodings[0].ssrc, + offer.SDP, + ) + assert.True( + t, + sdpMidHasSsrc(offer, "1", sender2.trackEncodings[0].ssrc), + "Expected mid %q with ssrc %d, offer.sdp: %s", + "1", + sender2.trackEncodings[0].ssrc, + offer.SDP, + ) closePairNow(t, pcOffer, pcAnswer) } @@ -440,7 +479,10 @@ func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) { sender1, err := pcOffer.AddTrack(track1) require.NoError(t, err) - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) require.NoError(t, err) tracksCh := make(chan *TrackRemote) @@ -450,6 +492,7 @@ func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) { for { if _, _, readErr := track.ReadRTP(); errors.Is(readErr, io.EOF) { tracksClosed <- struct{}{} + return } } @@ -467,7 +510,7 @@ func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) { require.Equal(t, "0", transceivers[0].Mid()) ctx, cancel := context.WithCancel(context.Background()) - go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track1}) + go sendVideoUntilDone(t, ctx.Done(), []*TrackLocalStaticSample{track1}) remoteTrack1 := <-tracksCh cancel() @@ -492,7 +535,7 @@ func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) { require.Equal(t, "0", transceivers[0].Mid()) ctx, cancel = context.WithCancel(context.Background()) - go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track2}) + go sendVideoUntilDone(t, ctx.Done(), []*TrackLocalStaticSample{track2}) remoteTrack2 := <-tracksCh cancel() @@ -521,7 +564,10 @@ func TestPeerConnection_Renegotiation_RemoveTrack(t *testing.T) { t.Fatal(err) } - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") @@ -539,13 +585,14 @@ func TestPeerConnection_Renegotiation_RemoveTrack(t *testing.T) { for { if _, _, err := track.ReadRTP(); errors.Is(err, io.EOF) { trackClosedFunc() + return } } }) assert.NoError(t, signalPair(pcOffer, pcAnswer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{vp8Track}) assert.NoError(t, pcOffer.RemoveTrack(sender)) assert.NoError(t, signalPair(pcOffer, pcAnswer)) @@ -574,8 +621,12 @@ func TestPeerConnection_RoleSwitch(t *testing.T) { assert.NoError(t, signalPair(pcFirstOfferer, pcSecondOfferer)) // Add a new Track to the second offerer - // This asserts that it will match the ordering of the last RemoteDescription, but then also add new Transceivers to the end - _, err = pcFirstOfferer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + // This asserts that it will match the ordering of the last RemoteDescription, + // but then also add new Transceivers to the end. + _, err = pcFirstOfferer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) vp8Track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") @@ -585,14 +636,14 @@ func TestPeerConnection_RoleSwitch(t *testing.T) { assert.NoError(t, err) assert.NoError(t, signalPair(pcSecondOfferer, pcFirstOfferer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{vp8Track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{vp8Track}) closePairNow(t, pcFirstOfferer, pcSecondOfferer) } // Assert that renegotiation doesn't attempt to gather ICE twice // Before we would attempt to gather multiple times and would put -// the PeerConnection into a broken state +// the PeerConnection into a broken state. func TestPeerConnection_Renegotiation_Trickle(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -681,7 +732,10 @@ func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) { pcOffer.ops.Done() pcAnswer.ops.Done() - _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcOffer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) assert.NoError(t, err) localTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "foo", "bar") @@ -709,13 +763,13 @@ func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) { assert.NoError(t, pcOffer.SetRemoteDescription(answer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{localTrack}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{localTrack}) closePairNow(t, pcOffer, pcAnswer) } // Issue #346, don't start the SCTP Subsystem if the RemoteDescription doesn't contain one -// Before we would always start it, and re-negotiations would fail because SCTP was in flight +// Before we would always start it, and re-negotiations would fail because SCTP was in flight. func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) { signalPairExcludeDataChannel := func(pcOffer, pcAnswer *PeerConnection) { offer, err := pcOffer.CreateOffer(nil) @@ -761,10 +815,16 @@ func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) { } }) - _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) + _, err = pcOffer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}, + ) assert.NoError(t, err) - _, err = pcAnswer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) + _, err = pcAnswer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}, + ) assert.NoError(t, err) signalPairExcludeDataChannel(pcOffer, pcAnswer) @@ -847,7 +907,7 @@ func TestAddDataChannelDuringRenegotiation(t *testing.T) { closePairNow(t, pcOffer, pcAnswer) } -// Assert that CreateDataChannel fires OnNegotiationNeeded +// Assert that CreateDataChannel fires OnNegotiationNeeded. func TestNegotiationCreateDataChannel(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -963,7 +1023,7 @@ func TestNegotiationNeededStressOneSided(t *testing.T) { } // TestPeerConnection_Renegotiation_DisableTrack asserts that if a remote track is set inactive -// that locally it goes inactive as well +// that locally it goes inactive as well. func TestPeerConnection_Renegotiation_DisableTrack(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -1020,6 +1080,7 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { for _, rid := range rids { sessionDescription += "a=" + sdpAttributeRid + ":" + rid + " send\r\n" } + return sessionDescription + "a=simulcast:send " + strings.Join(rids, ";") + "\r\n" } @@ -1046,7 +1107,7 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { for ssrc, rid := range rids { header := &rtp.Header{ Version: 2, - SSRC: uint32(ssrc + 1), + SSRC: uint32(ssrc + 1), //nolint:gosec // G115 SequenceNumber: sequenceNumber, PayloadType: 96, } @@ -1060,6 +1121,8 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { } assertTracksClosed := func(t *testing.T) { + t.Helper() + trackMapLock.Lock() defer trackMapLock.Unlock() @@ -1098,6 +1161,7 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { assert.NoError(t, pcOffer.RemoveTrack(rtpTransceiver.Sender())) assert.NoError(t, signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { sessionDescription = strings.SplitAfter(sessionDescription, "a=end-of-candidates\r\n")[0] + return sessionDescription })) @@ -1140,6 +1204,7 @@ func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) { sessionDescription += l + "\n" } + return signalWithRids(sessionDescription, newRids) })) @@ -1245,9 +1310,15 @@ func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) { _, err = offerPC.CreateDataChannel("test", nil) assert.NoError(t, err) - _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) + _, err = offerPC.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}, + ) assert.NoError(t, err) - _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeAudio, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) + _, err = offerPC.AddTransceiverFromKind( + RTPCodecTypeAudio, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}, + ) assert.NoError(t, err) offer, err := offerPC.CreateOffer(nil) @@ -1260,10 +1331,16 @@ func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) { assert.NoError(t, offerPC.SetRemoteDescription(answer)) assert.Equal(t, SignalingStateStable, offerPC.SignalingState()) - tr, err := offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}) + tr, err := offerPC.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendonly}, + ) assert.NoError(t, err) assert.NoError(t, tr.SetMid("3")) - _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}) + _, err = offerPC.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionSendrecv}, + ) assert.NoError(t, err) _, err = offerPC.CreateOffer(nil) assert.NoError(t, err) @@ -1316,7 +1393,7 @@ func TestPeerConnection_Regegotiation_AnswerAddsTrack(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - go sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{vp8Track}) + go sendVideoUntilDone(t, ctx.Done(), []*TrackLocalStaticSample{vp8Track}) <-tracksCh cancel() @@ -1340,7 +1417,10 @@ func TestNegotiationNeededWithRecvonlyTrack(t *testing.T) { wg.Add(1) pcAnswer.OnNegotiationNeeded(wg.Done) - _, err = pcOffer.AddTransceiverFromKind(RTPCodecTypeVideo, RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}) + _, err = pcOffer.AddTransceiverFromKind( + RTPCodecTypeVideo, + RTPTransceiverInit{Direction: RTPTransceiverDirectionRecvonly}, + ) if err != nil { t.Fatal(err) } diff --git a/peerconnection_test.go b/peerconnection_test.go index a87ba1fb..c2584c8e 100644 --- a/peerconnection_test.go +++ b/peerconnection_test.go @@ -32,7 +32,11 @@ func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) { return pca, pcb, nil } -func signalPairWithModification(pcOffer *PeerConnection, pcAnswer *PeerConnection, modificationFunc func(string) string) error { +func signalPairWithModification( + pcOffer *PeerConnection, + pcAnswer *PeerConnection, + modificationFunc func(string) string, +) error { // Note(albrow): We need to create a data channel in order to trigger ICE // candidate gathering in the background for the JavaScript/Wasm bindings. If // we don't do this, the complete offer including ICE candidates will never be @@ -65,11 +69,16 @@ func signalPairWithModification(pcOffer *PeerConnection, pcAnswer *PeerConnectio return err } <-answerGatheringComplete + return pcOffer.SetRemoteDescription(*pcAnswer.LocalDescription()) } func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error { - return signalPairWithModification(pcOffer, pcAnswer, func(sessionDescription string) string { return sessionDescription }) + return signalPairWithModification( + pcOffer, + pcAnswer, + func(sessionDescription string) string { return sessionDescription }, + ) } func offerMediaHasDirection(offer SessionDescription, kind RTPCodecType, direction RTPTransceiverDirection) bool { @@ -81,9 +90,11 @@ func offerMediaHasDirection(offer SessionDescription, kind RTPCodecType, directi for _, media := range parsed.MediaDescriptions { if media.MediaName.Media == kind.String() { _, exists := media.Attribute(direction.String()) + return exists } } + return false } @@ -103,6 +114,7 @@ func untilConnectionState(state PeerConnectionState, peers ...*PeerConnection) * p.OnConnectionStateChange(hdlr) } + return &triggered } @@ -179,6 +191,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) { err = pc.Close() assert.Nil(t, err) + return pc, err }, config: Configuration{}, @@ -231,6 +244,7 @@ func TestPeerConnection_SetConfiguration(t *testing.T) { if err != nil { return pc, err } + return pc, nil }, config: Configuration{ @@ -607,8 +621,8 @@ func TestMultipleCreateChannel(t *testing.T) { closePairNow(t, pcOffer, pcAnswer) } -// Assert that candidates are gathered by calling SetLocalDescription, not SetRemoteDescription -func TestGatherOnSetLocalDescription(t *testing.T) { +// Assert that candidates are gathered by calling SetLocalDescription, not SetRemoteDescription. +func TestGatherOnSetLocalDescription(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -678,7 +692,7 @@ func TestGatherOnSetLocalDescription(t *testing.T) { closePairNow(t, pcOffer, pcAnswer) } -// Assert that SetRemoteDescription handles invalid states +// Assert that SetRemoteDescription handles invalid states. func TestSetRemoteDescriptionInvalid(t *testing.T) { t.Run("local-offer+SetRemoteDescription(Offer)", func(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) @@ -738,7 +752,7 @@ func TestAddTransceiver(t *testing.T) { } } -// Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected +// Assert that SCTPTransport -> DTLSTransport -> ICETransport works after connected. func TestTransportChain(t *testing.T) { offer, answer, err := newPair() assert.NoError(t, err) @@ -752,7 +766,7 @@ func TestTransportChain(t *testing.T) { closePairNow(t, offer, answer) } -// Assert that the PeerConnection closes via DTLS (and not ICE) +// Assert that the PeerConnection closes via DTLS (and not ICE). func TestDTLSClose(t *testing.T) { lim := test.TimeOut(time.Second * 10) defer lim.Stop() diff --git a/peerconnectionstate.go b/peerconnectionstate.go index 0ff24afc..677cfa95 100644 --- a/peerconnectionstate.go +++ b/peerconnectionstate.go @@ -7,7 +7,7 @@ package webrtc type PeerConnectionState int const ( - // PeerConnectionStateUnknown is the enum's zero-value + // PeerConnectionStateUnknown is the enum's zero-value. PeerConnectionStateUnknown PeerConnectionState = iota // PeerConnectionStateNew indicates that any of the ICETransports or diff --git a/pkg/media/h264reader/h264reader.go b/pkg/media/h264reader/h264reader.go index c9975910..21400237 100644 --- a/pkg/media/h264reader/h264reader.go +++ b/pkg/media/h264reader/h264reader.go @@ -10,7 +10,7 @@ import ( "io" ) -// H264Reader reads data from stream and constructs h264 nal units +// H264Reader reads data from stream and constructs h264 nal units. type H264Reader struct { stream io.Reader nalBuffer []byte @@ -25,7 +25,7 @@ var ( errDataIsNotH264Stream = errors.New("data is not a H264 bitstream") ) -// NewReader creates new H264Reader +// NewReader creates new H264Reader. func NewReader(in io.Reader) (*H264Reader, error) { if in == nil { return nil, errNilReader @@ -42,7 +42,7 @@ func NewReader(in io.Reader) (*H264Reader, error) { return reader, nil } -// NAL H.264 Network Abstraction Layer +// NAL H.264 Network Abstraction Layer. type NAL struct { PictureOrderCount uint32 @@ -73,6 +73,7 @@ func (reader *H264Reader) read(numToRead int) (data []byte, e error) { } data = reader.readBuffer[0:numShouldRead] reader.readBuffer = reader.readBuffer[numShouldRead:] + return data, nil } @@ -82,7 +83,7 @@ func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLength int, e e prefixBuffer, e := reader.read(4) if e != nil { - return + return prefixLength, e } n := len(prefixBuffer) @@ -100,12 +101,14 @@ func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLength int, e e if nalPrefix3BytesFound { return 0, io.EOF } + return 0, errDataIsNotH264Stream } // n == 4 if nalPrefix3BytesFound { reader.nalBuffer = append(reader.nalBuffer, prefixBuffer[3]) + return 3, nil } @@ -113,6 +116,7 @@ func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLength int, e e if nalPrefix4BytesFound { return 4, nil } + return 0, errDataIsNotH264Stream } @@ -147,8 +151,10 @@ func (reader *H264Reader) NextNAL() (*NAL, error) { nal.parseHeader() if nal.UnitType == NalUnitTypeSEI { reader.nalBuffer = nil + continue } + break } diff --git a/pkg/media/h264reader/h264reader_test.go b/pkg/media/h264reader/h264reader_test.go index e5c1a318..9f6689e2 100644 --- a/pkg/media/h264reader/h264reader_test.go +++ b/pkg/media/h264reader/h264reader_test.go @@ -124,14 +124,14 @@ func TestIssue1734_NextNal(t *testing.T) { } func TestTrailing01AfterStartCode(t *testing.T) { - r, err := NewReader(bytes.NewReader([]byte{ + reader, err := NewReader(bytes.NewReader([]byte{ 0x0, 0x0, 0x0, 0x1, 0x01, 0x0, 0x0, 0x0, 0x1, 0x01, })) require.NoError(t, err) for i := 0; i <= 1; i++ { - nal, err := r.NextNAL() + nal, err := reader.NextNAL() require.NoError(t, err) require.NotNil(t, nal) } diff --git a/pkg/media/h264reader/nalunittype.go b/pkg/media/h264reader/nalunittype.go index 7d87342b..ff94236f 100644 --- a/pkg/media/h264reader/nalunittype.go +++ b/pkg/media/h264reader/nalunittype.go @@ -5,10 +5,10 @@ package h264reader import "strconv" -// NalUnitType is the type of a NAL +// NalUnitType is the type of a NAL. type NalUnitType uint8 -// Enums for NalUnitTypes +// Enums for NalUnitTypes. const ( NalUnitTypeUnspecified NalUnitType = 0 // Unspecified NalUnitTypeCodedSliceNonIdr NalUnitType = 1 // Coded slice of a non-IDR picture @@ -25,12 +25,12 @@ const ( NalUnitTypeFiller NalUnitType = 12 // Filler data NalUnitTypeSpsExt NalUnitType = 13 // Sequence parameter set extension NalUnitTypeCodedSliceAux NalUnitType = 19 // Coded slice of an auxiliary coded picture without partitioning - // 14..18 // Reserved - // 20..23 // Reserved - // 24..31 // Unspecified + // 14..18 // Reserved. + // 20..23 // Reserved. + // 24..31 // Unspecified. ) -func (n *NalUnitType) String() string { +func (n *NalUnitType) String() string { //nolint:cyclop var str string switch *n { case NalUnitTypeUnspecified: @@ -67,5 +67,6 @@ func (n *NalUnitType) String() string { str = "Unknown" } str = str + "(" + strconv.FormatInt(int64(*n), 10) + ")" + return str } diff --git a/pkg/media/h264writer/h264writer.go b/pkg/media/h264writer/h264writer.go index cc8f64ba..22da0d3e 100644 --- a/pkg/media/h264writer/h264writer.go +++ b/pkg/media/h264writer/h264writer.go @@ -27,7 +27,7 @@ type ( } ) -// New builds a new H264 writer +// New builds a new H264 writer. func New(filename string) (*H264Writer, error) { f, err := os.Create(filename) //nolint:gosec if err != nil { @@ -37,14 +37,14 @@ func New(filename string) (*H264Writer, error) { return NewWith(f), nil } -// NewWith initializes a new H264 writer with an io.Writer output +// NewWith initializes a new H264 writer with an io.Writer output. func NewWith(w io.Writer) *H264Writer { return &H264Writer{ writer: w, } } -// WriteRTP adds a new packet and writes the appropriate headers for it +// WriteRTP adds a new packet and writes the appropriate headers for it. func (h *H264Writer) WriteRTP(packet *rtp.Packet) error { if len(packet.Payload) == 0 { return nil @@ -71,7 +71,7 @@ func (h *H264Writer) WriteRTP(packet *rtp.Packet) error { return err } -// Close closes the underlying writer +// Close closes the underlying writer. func (h *H264Writer) Close() error { h.cachedPacket = nil if h.writer != nil { diff --git a/pkg/media/ivfreader/ivfreader.go b/pkg/media/ivfreader/ivfreader.go index 4f473ece..0c4a3434 100644 --- a/pkg/media/ivfreader/ivfreader.go +++ b/pkg/media/ivfreader/ivfreader.go @@ -49,7 +49,7 @@ type IVFFrameHeader struct { Timestamp uint64 // 4-11 } -// IVFReader is used to read IVF files and return frame payloads +// IVFReader is used to read IVF files and return frame payloads. type IVFReader struct { stream io.Reader bytesReadSuccesfully int64 @@ -58,14 +58,14 @@ type IVFReader struct { } // NewWith returns a new IVF reader and IVF file header -// with an io.Reader input -func NewWith(in io.Reader) (*IVFReader, *IVFFileHeader, error) { - if in == nil { +// with an io.Reader input. +func NewWith(stream io.Reader) (*IVFReader, *IVFFileHeader, error) { + if stream == nil { return nil, nil, errNilStream } reader := &IVFReader{ - stream: in, + stream: stream, } header, err := reader.parseFileHeader() @@ -122,11 +122,12 @@ func (i *IVFReader) ParseNextFrame() ([]byte, *IVFFrameHeader, error) { } i.bytesReadSuccesfully += int64(headerBytesRead) + int64(bytesRead) + return payload, header, nil } // parseFileHeader reads 32 bytes from stream and returns -// IVF file header. This is always called before ParseNextFrame() +// IVF file header. This is always called before ParseNextFrame(). func (i *IVFReader) parseFileHeader() (*IVFFileHeader, error) { buffer := make([]byte, ivfFileHeaderSize) @@ -157,5 +158,6 @@ func (i *IVFReader) parseFileHeader() (*IVFFileHeader, error) { } i.bytesReadSuccesfully += int64(bytesRead) + return header, nil } diff --git a/pkg/media/ivfreader/ivfreader_test.go b/pkg/media/ivfreader/ivfreader_test.go index f1b8c517..20cf37ff 100644 --- a/pkg/media/ivfreader/ivfreader_test.go +++ b/pkg/media/ivfreader/ivfreader_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -// buildIVFContainer takes frames and prepends valid IVF file header +// buildIVFContainer takes frames and prepends valid IVF file header. func buildIVFContainer(frames ...*[]byte) *bytes.Buffer { // Valid IVF file header taken from: https://github.com/webmproject/... // vp8-test-vectors/blob/master/vp80-00-comprehensive-001.ivf diff --git a/pkg/media/ivfwriter/ivfwriter.go b/pkg/media/ivfwriter/ivfwriter.go index 9138227b..5bc5a0ae 100644 --- a/pkg/media/ivfwriter/ivfwriter.go +++ b/pkg/media/ivfwriter/ivfwriter.go @@ -31,7 +31,7 @@ const ( var errInvalidMediaTimebase = errors.New("invalid media timebase") -// IVFWriter is used to take RTP packets and write them to an IVF on disk +// IVFWriter is used to take RTP packets and write them to an IVF on disk. type IVFWriter struct { ioWriter io.Writer count uint64 @@ -51,21 +51,22 @@ type IVFWriter struct { av1Frame frame.AV1 } -// New builds a new IVF writer +// New builds a new IVF writer. func New(fileName string, opts ...Option) (*IVFWriter, error) { - f, err := os.Create(fileName) //nolint:gosec + file, err := os.Create(fileName) //nolint:gosec if err != nil { return nil, err } - writer, err := NewWith(f, opts...) + writer, err := NewWith(file, opts...) if err != nil { return nil, err } - writer.ioWriter = f + writer.ioWriter = file + return writer, nil } -// NewWith initialize a new IVF writer with an io.Writer output +// NewWith initialize a new IVF writer with an io.Writer output. func NewWith(out io.Writer, opts ...Option) (*IVFWriter, error) { if out == nil { return nil, errFileNotOpened @@ -96,6 +97,7 @@ func NewWith(out io.Writer, opts ...Option) (*IVFWriter, error) { if writer.timebaseDenominator == 0 { return nil, errInvalidMediaTimebase } + return writer, nil } @@ -120,6 +122,7 @@ func (i *IVFWriter) writeHeader() error { binary.LittleEndian.PutUint32(header[28:], 0) // Unused _, err := i.ioWriter.Write(header) + return err } @@ -129,6 +132,7 @@ func (i *IVFWriter) timestampToPts(timestamp uint64) uint64 { func (i *IVFWriter) writeFrame(frame []byte, timestamp uint64) error { frameHeader := make([]byte, 12) + //nolint:gosec // G115 binary.LittleEndian.PutUint32(frameHeader[0:], uint32(len(frame))) // Frame length binary.LittleEndian.PutUint64(frameHeader[4:], i.timestampToPts(timestamp)) // PTS i.count++ @@ -137,11 +141,12 @@ func (i *IVFWriter) writeFrame(frame []byte, timestamp uint64) error { return err } _, err := i.ioWriter.Write(frame) + return err } -// WriteRTP adds a new packet and writes the appropriate headers for it -func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error { +// WriteRTP adds a new packet and writes the appropriate headers for it. +func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error { //nolint:cyclop if i.ioWriter == nil { return errFileNotOpened } else if len(packet.Payload) == 0 { @@ -153,7 +158,7 @@ func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error { } relativeTstampMs := 1000 * uint64(packet.Header.Timestamp-i.firstFrameTimestamp) / i.clockRate - if i.isVP8 { + if i.isVP8 { //nolint:nestif vp8Packet := codecs.VP8Packet{} if _, err := vp8Packet.Unmarshal(packet.Payload); err != nil { return err @@ -201,7 +206,7 @@ func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error { return nil } -// Close stops the recording +// Close stops the recording. func (i *IVFWriter) Close() error { if i.ioWriter == nil { // Returns no error as it may be convenient to call @@ -219,7 +224,7 @@ func (i *IVFWriter) Close() error { return err } buff := make([]byte, 4) - binary.LittleEndian.PutUint32(buff, uint32(i.count)) + binary.LittleEndian.PutUint32(buff, uint32(i.count)) //nolint:gosec // G115 if _, err := ws.Write(buff); err != nil { return err } @@ -235,7 +240,7 @@ func (i *IVFWriter) Close() error { // An Option configures a SampleBuilder. type Option func(i *IVFWriter) error -// WithCodec configures if IVFWriter is writing AV1 or VP8 packets to disk +// WithCodec configures if IVFWriter is writing AV1 or VP8 packets to disk. func WithCodec(mimeType string) Option { return func(i *IVFWriter) error { if i.isVP8 || i.isAV1 { diff --git a/pkg/media/ivfwriter/ivfwriter_test.go b/pkg/media/ivfwriter/ivfwriter_test.go index 89395436..61663025 100644 --- a/pkg/media/ivfwriter/ivfwriter_test.go +++ b/pkg/media/ivfwriter/ivfwriter_test.go @@ -215,15 +215,18 @@ func TestIVFWriter_VP8(t *testing.T) { // Third test tries to write a valid VP8 packet - No Keyframe assert.False(addPacketTestCase[0].writer.seenKeyFrame, "Writer's seenKeyFrame should remain false") assert.Equal(uint64(0), addPacketTestCase[0].writer.count, "Writer's packet count should remain 0") - assert.Equal(nil, addPacketTestCase[0].writer.WriteRTP(midPartPacket), "Write packet failed") // add a mid partition packet + // add a mid partition packet + assert.Equal(nil, addPacketTestCase[0].writer.WriteRTP(midPartPacket), "Write packet failed") assert.Equal(uint64(0), addPacketTestCase[0].writer.count, "Writer's packet count should remain 0") // Fifth test tries to write a keyframe packet assert.True(addPacketTestCase[1].writer.seenKeyFrame, "Writer's seenKeyFrame should now be true") assert.Equal(uint64(1), addPacketTestCase[1].writer.count, "Writer's packet count should now be 1") - assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(midPartPacket), "Write packet failed") // add a mid partition packet + // add a mid partition packet + assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(midPartPacket), "Write packet failed") assert.Equal(uint64(1), addPacketTestCase[1].writer.count, "Writer's packet count should remain 1") - assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(validPacket), "Write packet failed") // add a valid packet + // add a valid packet + assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(validPacket), "Write packet failed") assert.Equal(uint64(2), addPacketTestCase[1].writer.count, "Writer's packet count should now be 2") for _, t := range addPacketTestCase { diff --git a/pkg/media/media.go b/pkg/media/media.go index ab07e318..f8ee5e38 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -10,7 +10,7 @@ import ( "github.com/pion/rtp" ) -// A Sample contains encoded media and timing information +// A Sample contains encoded media and timing information. type Sample struct { Data []byte Timestamp time.Time @@ -25,7 +25,7 @@ type Sample struct { } // Writer defines an interface to handle -// the creation of media files +// the creation of media files. type Writer interface { // Add the content of an RTP packet to the media WriteRTP(packet *rtp.Packet) error diff --git a/pkg/media/oggreader/oggreader.go b/pkg/media/oggreader/oggreader.go index 5aaeb8b9..905a3da6 100644 --- a/pkg/media/oggreader/oggreader.go +++ b/pkg/media/oggreader/oggreader.go @@ -30,7 +30,7 @@ var ( errChecksumMismatch = errors.New("expected and actual checksum do not match") ) -// OggReader is used to read Ogg files and return page payloads +// OggReader is used to read Ogg files and return page payloads. type OggReader struct { stream io.Reader bytesReadSuccesfully int64 @@ -67,7 +67,7 @@ type OggPageHeader struct { } // NewWith returns a new Ogg reader and Ogg header -// with an io.Reader input +// with an io.Reader input. func NewWith(in io.Reader) (*OggReader, *OggHeader, error) { return newWith(in /* doChecksum */, true) } @@ -126,26 +126,26 @@ func (o *OggReader) readHeaders() (*OggHeader, error) { // ParseNextPage reads from stream and returns Ogg page payload, header, // and an error if there is incomplete page data. -func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { - h := make([]byte, pageHeaderLen) +func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { //nolint:cyclop + header := make([]byte, pageHeaderLen) - n, err := io.ReadFull(o.stream, h) + n, err := io.ReadFull(o.stream, header) if err != nil { return nil, nil, err - } else if n < len(h) { + } else if n < len(header) { return nil, nil, errShortPageHeader } pageHeader := &OggPageHeader{ - sig: [4]byte{h[0], h[1], h[2], h[3]}, + sig: [4]byte{header[0], header[1], header[2], header[3]}, } - pageHeader.version = h[4] - pageHeader.headerType = h[5] - pageHeader.GranulePosition = binary.LittleEndian.Uint64(h[6 : 6+8]) - pageHeader.serial = binary.LittleEndian.Uint32(h[14 : 14+4]) - pageHeader.index = binary.LittleEndian.Uint32(h[18 : 18+4]) - pageHeader.segmentsCount = h[26] + pageHeader.version = header[4] + pageHeader.headerType = header[5] + pageHeader.GranulePosition = binary.LittleEndian.Uint64(header[6 : 6+8]) + pageHeader.serial = binary.LittleEndian.Uint32(header[14 : 14+4]) + pageHeader.index = binary.LittleEndian.Uint32(header[18 : 18+4]) + pageHeader.segmentsCount = header[26] sizeBuffer := make([]byte, pageHeader.segmentsCount) if _, err = io.ReadFull(o.stream, sizeBuffer); err != nil { @@ -168,14 +168,15 @@ func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { checksum = (checksum << 8) ^ o.checksumTable[byte(checksum>>24)^v] } - for index := range h { + for index := range header { // Don't include expected checksum in our generation if index > 21 && index < 26 { updateChecksum(0) + continue } - updateChecksum(h[index]) + updateChecksum(header[index]) } for _, s := range sizeBuffer { updateChecksum(s) @@ -184,12 +185,12 @@ func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { updateChecksum(payload[index]) } - if binary.LittleEndian.Uint32(h[22:22+4]) != checksum { + if binary.LittleEndian.Uint32(header[22:22+4]) != checksum { return nil, nil, errChecksumMismatch } } - o.bytesReadSuccesfully += int64(len(h) + len(sizeBuffer) + len(payload)) + o.bytesReadSuccesfully += int64(len(header) + len(sizeBuffer) + len(payload)) return payload, pageHeader, nil } @@ -206,7 +207,7 @@ func generateChecksumTable() *[256]uint32 { const poly = 0x04c11db7 for i := range table { - r := uint32(i) << 24 + r := uint32(i) << 24 //nolint:gosec // G115 for j := 0; j < 8; j++ { if (r & 0x80000000) != 0 { r = (r << 1) ^ poly @@ -216,5 +217,6 @@ func generateChecksumTable() *[256]uint32 { table[i] = (r & 0xffffffff) } } + return &table } diff --git a/pkg/media/oggreader/oggreader_test.go b/pkg/media/oggreader/oggreader_test.go index cbb151e9..88a1e692 100644 --- a/pkg/media/oggreader/oggreader_test.go +++ b/pkg/media/oggreader/oggreader_test.go @@ -12,7 +12,7 @@ import ( ) // buildOggFile generates a valid oggfile that can -// be used for tests +// be used for tests. func buildOggContainer() []byte { return []byte{ 0x4f, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, diff --git a/pkg/media/oggwriter/oggwriter.go b/pkg/media/oggwriter/oggwriter.go index 0852e380..8015f71f 100644 --- a/pkg/media/oggwriter/oggwriter.go +++ b/pkg/media/oggwriter/oggwriter.go @@ -30,7 +30,7 @@ var ( errInvalidNilPacket = errors.New("invalid nil packet") ) -// OggWriter is used to take RTP packets and write them to an OGG on disk +// OggWriter is used to take RTP packets and write them to an OGG on disk. type OggWriter struct { stream io.Writer fd *os.File @@ -44,21 +44,22 @@ type OggWriter struct { lastPayloadSize int } -// New builds a new OGG Opus writer +// New builds a new OGG Opus writer. func New(fileName string, sampleRate uint32, channelCount uint16) (*OggWriter, error) { - f, err := os.Create(fileName) //nolint:gosec + file, err := os.Create(fileName) //nolint:gosec if err != nil { return nil, err } - writer, err := NewWith(f, sampleRate, channelCount) + writer, err := NewWith(file, sampleRate, channelCount) if err != nil { - return nil, f.Close() + return nil, file.Close() } - writer.fd = f + writer.fd = file + return writer, nil } -// NewWith initialize a new OGG Opus writer with an io.Writer output +// NewWith initialize a new OGG Opus writer with an io.Writer output. func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, error) { if out == nil { return nil, errFileNotOpened @@ -110,8 +111,9 @@ func (i *OggWriter) writeHeaders() error { // ID Header oggIDHeader := make([]byte, 19) - copy(oggIDHeader[0:], idPageSignature) // Magic Signature 'OpusHead' - oggIDHeader[8] = 1 // Version + copy(oggIDHeader[0:], idPageSignature) // Magic Signature 'OpusHead' + oggIDHeader[8] = 1 // Version + //nolint:gosec // G115 oggIDHeader[9] = uint8(i.channelCount) // Channel count binary.LittleEndian.PutUint16(oggIDHeader[10:], defaultPreSkip) // pre-skip binary.LittleEndian.PutUint32(oggIDHeader[12:], i.sampleRate) // original sample rate, any valid sample e.g 48000 @@ -159,7 +161,8 @@ func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uin binary.LittleEndian.PutUint64(page[6:], granulePos) // granule position binary.LittleEndian.PutUint32(page[14:], i.serial) // Bitstream serial number binary.LittleEndian.PutUint32(page[18:], pageIndex) // Page sequence number - page[26] = uint8(nSegments) // Number of segments in page. + //nolint:gosec // G115 + page[26] = uint8(nSegments) // Number of segments in page. // Filling segment table with the lacing values. // First (nSegments - 1) values will always be 255. @@ -167,7 +170,7 @@ func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uin page[pageHeaderSize+i] = 255 } // The last value will be the remainder. - page[pageHeaderSize+nSegments-1] = uint8(len(payload) % 255) + page[pageHeaderSize+nSegments-1] = uint8(len(payload) % 255) //nolint:gosec // G115 copy(page[pageHeaderSize+nSegments:], payload) // Payload goes after the segment table, so at pageHeaderSize+nSegments. @@ -176,12 +179,13 @@ func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uin checksum = (checksum << 8) ^ i.checksumTable[byte(checksum>>24)^page[index]] } - binary.LittleEndian.PutUint32(page[22:], checksum) // Checksum - generating for page data and inserting at 22th position into 32 bits + // Checksum - generating for page data and inserting at 22th position into 32 bits + binary.LittleEndian.PutUint32(page[22:], checksum) return page } -// WriteRTP adds a new packet and writes the appropriate headers for it +// WriteRTP adds a new packet and writes the appropriate headers for it. func (i *OggWriter) WriteRTP(packet *rtp.Packet) error { if packet == nil { return errInvalidNilPacket @@ -207,10 +211,11 @@ func (i *OggWriter) WriteRTP(packet *rtp.Packet) error { data := i.createPage(payload, pageHeaderTypeContinuationOfStream, i.previousGranulePosition, i.pageIndex) i.pageIndex++ + return i.writeToStream(data) } -// Close stops the recording +// Close stops the recording. func (i *OggWriter) Close() error { defer func() { i.fd = nil @@ -224,6 +229,7 @@ func (i *OggWriter) Close() error { if closer, ok := i.stream.(io.Closer); ok { return closer.Close() } + return nil } @@ -249,13 +255,14 @@ func (i *OggWriter) Close() error { } // Wraps writing to the stream and maintains state -// so we can set values for EOS +// so we can set values for EOS. func (i *OggWriter) writeToStream(p []byte) error { if i.stream == nil { return errFileNotOpened } _, err := i.stream.Write(p) + return err } @@ -264,15 +271,16 @@ func generateChecksumTable() *[256]uint32 { const poly = 0x04c11db7 for i := range table { - r := uint32(i) << 24 + remainder := uint32(i) << 24 //nolint:gosec // G115 for j := 0; j < 8; j++ { - if (r & 0x80000000) != 0 { - r = (r << 1) ^ poly + if (remainder & 0x80000000) != 0 { + remainder = (remainder << 1) ^ poly } else { - r <<= 1 + remainder <<= 1 } - table[i] = (r & 0xffffffff) + table[i] = (remainder & 0xffffffff) } } + return &table } diff --git a/pkg/media/rtpdump/reader.go b/pkg/media/rtpdump/reader.go index 13b03623..5725fc49 100644 --- a/pkg/media/rtpdump/reader.go +++ b/pkg/media/rtpdump/reader.go @@ -11,7 +11,7 @@ import ( "sync" ) -// Reader reads the RTPDump file format +// Reader reads the RTPDump file format. type Reader struct { readerMu sync.Mutex reader io.Reader @@ -66,7 +66,7 @@ func NewReader(r io.Reader) (*Reader, Header, error) { }, hdr, nil } -// Next returns the next Packet in the Reader input stream +// Next returns the next Packet in the Reader input stream. func (r *Reader) Next() (Packet, error) { r.readerMu.Lock() defer r.readerMu.Unlock() @@ -81,16 +81,16 @@ func (r *Reader) Next() (Packet, error) { return Packet{}, err } - var h packetHeader - if err = h.Unmarshal(hBuf); err != nil { + var header packetHeader + if err = header.Unmarshal(hBuf); err != nil { return Packet{}, err } - if h.Length == 0 { + if header.Length == 0 { return Packet{}, errMalformed } - payload := make([]byte, h.Length-pktHeaderLen) + payload := make([]byte, header.Length-pktHeaderLen) _, err = io.ReadFull(r.reader, payload) if errors.Is(err, io.ErrUnexpectedEOF) { return Packet{}, errMalformed @@ -100,8 +100,8 @@ func (r *Reader) Next() (Packet, error) { } return Packet{ - Offset: h.offset(), - IsRTCP: h.PacketLength == 0, + Offset: header.offset(), + IsRTCP: header.PacketLength == 0, Payload: payload, }, nil } diff --git a/pkg/media/rtpdump/reader_test.go b/pkg/media/rtpdump/reader_test.go index 1d7884a5..d79de5b4 100644 --- a/pkg/media/rtpdump/reader_test.go +++ b/pkg/media/rtpdump/reader_test.go @@ -13,7 +13,7 @@ import ( "time" ) -func TestReader(t *testing.T) { +func TestReader(t *testing.T) { //nolint:maintidx validPreamble := []byte("#!rtpplay1.0 224.2.0.1/3456\n") for _, test := range []struct { @@ -249,11 +249,12 @@ func TestReader(t *testing.T) { WantErr: nil, }, } { - r, hdr, err := NewReader(bytes.NewReader(test.Data)) + reader, hdr, err := NewReader(bytes.NewReader(test.Data)) if err != nil { if got, want := err, test.WantErr; !errors.Is(got, want) { t.Fatalf("NewReader(%s) err=%v want %v", test.Name, got, want) } + continue } @@ -264,12 +265,13 @@ func TestReader(t *testing.T) { var nextErr error var packets []Packet for { - pkt, err := r.Next() + pkt, err := reader.Next() if errors.Is(err, io.EOF) { break } if err != nil { nextErr = err + break } diff --git a/pkg/media/rtpdump/rtpdump.go b/pkg/media/rtpdump/rtpdump.go index e9361b01..44c07a5b 100644 --- a/pkg/media/rtpdump/rtpdump.go +++ b/pkg/media/rtpdump/rtpdump.go @@ -34,39 +34,39 @@ type Header struct { // Marshal encodes the Header as binary. func (h Header) Marshal() ([]byte, error) { - d := make([]byte, headerLen) + data := make([]byte, headerLen) startNano := h.Start.UnixNano() - startSec := uint32(startNano / int64(time.Second)) - startUsec := uint32( + startSec := uint32(startNano / int64(time.Second)) //nolint:gosec // G115 + startUsec := uint32( //nolint:gosec // G115 (startNano % int64(time.Second)) / int64(time.Microsecond), ) - binary.BigEndian.PutUint32(d[0:], startSec) - binary.BigEndian.PutUint32(d[4:], startUsec) + binary.BigEndian.PutUint32(data[0:], startSec) + binary.BigEndian.PutUint32(data[4:], startUsec) source := h.Source.To4() - copy(d[8:], source) + copy(data[8:], source) - binary.BigEndian.PutUint16(d[12:], h.Port) + binary.BigEndian.PutUint16(data[12:], h.Port) - return d, nil + return data, nil } // Unmarshal decodes the Header from binary. -func (h *Header) Unmarshal(d []byte) error { - if len(d) < headerLen { +func (h *Header) Unmarshal(data []byte) error { + if len(data) < headerLen { return errMalformed } // time as a `struct timeval` - startSec := binary.BigEndian.Uint32(d[0:]) - startUsec := binary.BigEndian.Uint32(d[4:]) + startSec := binary.BigEndian.Uint32(data[0:]) + startUsec := binary.BigEndian.Uint32(data[4:]) h.Start = time.Unix(int64(startSec), int64(startUsec)*1e3).UTC() // ipv4 address - h.Source = net.IPv4(d[8], d[9], d[10], d[11]) + h.Source = net.IPv4(data[8], data[9], data[10], data[11]) - h.Port = binary.BigEndian.Uint16(d[12:]) + h.Port = binary.BigEndian.Uint16(data[12:]) // 2 bytes of padding (ignored) @@ -94,8 +94,8 @@ func (p Packet) Marshal() ([]byte, error) { } hdr := packetHeader{ - Length: uint16(len(p.Payload)) + 8, - PacketLength: uint16(packetLength), + Length: uint16(len(p.Payload)) + 8, //nolint:gosec // G115 + PacketLength: uint16(packetLength), //nolint:gosec // G115 Offset: p.offsetMs(), } hdrData, err := hdr.Marshal() @@ -107,9 +107,9 @@ func (p Packet) Marshal() ([]byte, error) { } // Unmarshal decodes the Packet from binary. -func (p *Packet) Unmarshal(d []byte) error { +func (p *Packet) Unmarshal(data []byte) error { var hdr packetHeader - if err := hdr.Unmarshal(d); err != nil { + if err := hdr.Unmarshal(data); err != nil { return err } @@ -119,16 +119,16 @@ func (p *Packet) Unmarshal(d []byte) error { if hdr.Length < 8 { return errMalformed } - if len(d) < int(hdr.Length) { + if len(data) < int(hdr.Length) { return errMalformed } - p.Payload = d[8:hdr.Length] + p.Payload = data[8:hdr.Length] return nil } func (p *Packet) offsetMs() uint32 { - return uint32(p.Offset / time.Millisecond) + return uint32(p.Offset / time.Millisecond) //nolint:gosec // G115 } type packetHeader struct { diff --git a/pkg/media/rtpdump/rtpdump_test.go b/pkg/media/rtpdump/rtpdump_test.go index 8199ba67..2ec79329 100644 --- a/pkg/media/rtpdump/rtpdump_test.go +++ b/pkg/media/rtpdump/rtpdump_test.go @@ -105,13 +105,13 @@ func TestPacketRoundTrip(t *testing.T) { }, }, } { - d, err := test.Packet.Marshal() + packet, err := test.Packet.Marshal() if err != nil { t.Fatal(err) } var pkt Packet - if err := pkt.Unmarshal(d); err != nil { + if err := pkt.Unmarshal(packet); err != nil { t.Fatal(err) } diff --git a/pkg/media/rtpdump/writer.go b/pkg/media/rtpdump/writer.go index 9df63254..6dfa58be 100644 --- a/pkg/media/rtpdump/writer.go +++ b/pkg/media/rtpdump/writer.go @@ -9,7 +9,7 @@ import ( "sync" ) -// Writer writes the RTPDump file format +// Writer writes the RTPDump file format. type Writer struct { writerMu sync.Mutex writer io.Writer @@ -37,7 +37,7 @@ func NewWriter(w io.Writer, hdr Header) (*Writer, error) { return &Writer{writer: w}, nil } -// WritePacket writes a Packet to the output +// WritePacket writes a Packet to the output. func (w *Writer) WritePacket(p Packet) error { w.writerMu.Lock() defer w.writerMu.Unlock() diff --git a/pkg/media/samplebuilder/sampleSequenceLocation.go b/pkg/media/samplebuilder/sampleSequenceLocation.go index c20df810..7320f999 100644 --- a/pkg/media/samplebuilder/sampleSequenceLocation.go +++ b/pkg/media/samplebuilder/sampleSequenceLocation.go @@ -49,5 +49,6 @@ func (l sampleSequenceLocation) compare(pos uint16) int { if l.head-pos <= pos-l.tail { return slCompareBefore } + return slCompareAfter } diff --git a/pkg/media/samplebuilder/samplebuilder.go b/pkg/media/samplebuilder/samplebuilder.go index 170c00f0..a9a6eed3 100644 --- a/pkg/media/samplebuilder/samplebuilder.go +++ b/pkg/media/samplebuilder/samplebuilder.go @@ -64,6 +64,7 @@ func New(maxLate uint16, depacketizer rtp.Depacketizer, sampleRate uint32, opts for _, o := range opts { o(s) } + return s } @@ -78,6 +79,7 @@ func (s *SampleBuilder) tooOld(location sampleSequenceLocation) bool { for i := location.head; i != location.tail; i++ { if packet := s.buffer[i]; packet != nil { foundHead = packet + break } } @@ -89,6 +91,7 @@ func (s *SampleBuilder) tooOld(location sampleSequenceLocation) bool { for i := location.tail - 1; i != location.head; i-- { if packet := s.buffer[i]; packet != nil { foundTail = packet + break } } @@ -100,7 +103,7 @@ func (s *SampleBuilder) tooOld(location sampleSequenceLocation) bool { return timestampDistance(foundHead.Timestamp, foundTail.Timestamp) > s.maxLateTimestamp } -// fetchTimestamp returns the timestamp associated with a given sample location +// fetchTimestamp returns the timestamp associated with a given sample location. func (s *SampleBuilder) fetchTimestamp(location sampleSequenceLocation) (timestamp uint32, hasData bool) { if location.empty() { return 0, false @@ -109,6 +112,7 @@ func (s *SampleBuilder) fetchTimestamp(location sampleSequenceLocation) (timesta if packet == nil { return 0, false } + return packet.Timestamp, true } @@ -138,6 +142,7 @@ func (s *SampleBuilder) purgeConsumedLocation(consume sampleSequenceLocation, fo if !forceConsume { break } + fallthrough case slCompareBefore: s.releasePacket(s.filled.head) @@ -176,18 +181,18 @@ func (s *SampleBuilder) purgeBuffers(flush bool) { // Push adds an RTP Packet to s's buffer. // // Push does not copy the input. If you wish to reuse -// this memory make sure to copy before calling Push -func (s *SampleBuilder) Push(p *rtp.Packet) { - s.buffer[p.SequenceNumber] = p +// this memory make sure to copy before calling Push. +func (s *SampleBuilder) Push(packet *rtp.Packet) { + s.buffer[packet.SequenceNumber] = packet - switch s.filled.compare(p.SequenceNumber) { + switch s.filled.compare(packet.SequenceNumber) { case slCompareVoid: - s.filled.head = p.SequenceNumber - s.filled.tail = p.SequenceNumber + 1 + s.filled.head = packet.SequenceNumber + s.filled.tail = packet.SequenceNumber + 1 case slCompareBefore: - s.filled.head = p.SequenceNumber + s.filled.head = packet.SequenceNumber case slCompareAfter: - s.filled.tail = p.SequenceNumber + 1 + s.filled.tail = packet.SequenceNumber + 1 case slCompareInside: break } @@ -204,7 +209,8 @@ const secondToNanoseconds = 1000000000 // buildSample creates a sample from a valid collection of RTP Packets by // walking forwards building a sample if everything looks good clear and // update buffer+values -// nolint: gocognit +// +//nolint:gocognit,cyclop func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { if s.active.empty() { s.active = s.filled @@ -224,12 +230,14 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { if s.depacketizer.IsPartitionTail(s.buffer[i].Marker, s.buffer[i].Payload) { consume.head = s.active.head consume.tail = i + 1 + break } headTimestamp, hasData := s.fetchTimestamp(s.active) if hasData && s.buffer[i].Timestamp != headTimestamp { consume.head = s.active.head consume.tail = i + break } } @@ -252,6 +260,7 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { for i := consume.tail; i < s.active.tail; i++ { if s.buffer[i] != nil { afterTimestamp = s.buffer[i].Timestamp + break } } @@ -274,6 +283,7 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { } s.purgeConsumedLocation(consume, true) s.purgeConsumedBuffers() + return nil } @@ -282,7 +292,7 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { var metadata interface{} var rtpHeaders []*rtp.Header for i := consume.head; i != consume.tail; i++ { - p, err := s.depacketizer.Unmarshal(s.buffer[i].Payload) + payload, err := s.depacketizer.Unmarshal(s.buffer[i].Payload) if err != nil { return nil } @@ -294,7 +304,7 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample { rtpHeaders = append(rtpHeaders, &h) } - data = append(data, p...) + data = append(data, payload...) } samples := afterTimestamp - sampleTimestamp @@ -331,12 +341,13 @@ func (s *SampleBuilder) Pop() *media.Sample { var result *media.Sample result, s.preparedSamples[s.prepared.head] = s.preparedSamples[s.prepared.head], nil s.prepared.head++ + return result } -// seqnumDistance computes the distance between two sequence numbers +// seqnumDistance computes the distance between two sequence numbers. func seqnumDistance(x, y uint16) uint16 { - diff := int16(x - y) + diff := int16(x - y) //nolint:gosec // G115 if diff < 0 { return uint16(-diff) } @@ -344,9 +355,9 @@ func seqnumDistance(x, y uint16) uint16 { return uint16(diff) } -// timestampDistance computes the distance between two timestamps +// timestampDistance computes the distance between two timestamps. func timestampDistance(x, y uint32) uint32 { - diff := int32(x - y) + diff := int32(x - y) //nolint:gosec // G115 if diff < 0 { return uint32(-diff) } @@ -366,7 +377,7 @@ func WithPacketReleaseHandler(h func(*rtp.Packet)) Option { } // WithPacketHeadHandler set a head packet handler to allow inspecting -// the packet to extract certain information and return as custom metadata +// the packet to extract certain information and return as custom metadata. func WithPacketHeadHandler(h func(headPacket interface{}) interface{}) Option { return func(o *SampleBuilder) { o.packetHeadHandler = h @@ -378,7 +389,7 @@ func WithPacketHeadHandler(h func(headPacket interface{}) interface{}) Option { func WithMaxTimeDelay(maxLateDuration time.Duration) Option { return func(o *SampleBuilder) { totalMillis := maxLateDuration.Milliseconds() - o.maxLateTimestamp = uint32(int64(o.sampleRate) * totalMillis / 1000) + o.maxLateTimestamp = uint32(int64(o.sampleRate) * totalMillis / 1000) //nolint:gosec // G5G115 } } diff --git a/pkg/media/samplebuilder/samplebuilder_test.go b/pkg/media/samplebuilder/samplebuilder_test.go index 655ecb85..159d9786 100644 --- a/pkg/media/samplebuilder/samplebuilder_test.go +++ b/pkg/media/samplebuilder/samplebuilder_test.go @@ -57,6 +57,7 @@ func (f *fakeDepacketizer) IsPartitionHead(payload []byte) bool { return true } } + return false } @@ -64,7 +65,7 @@ func (f *fakeDepacketizer) IsPartitionTail(marker bool, _ []byte) bool { return marker } -func TestSampleBuilder(t *testing.T) { +func TestSampleBuilder(t *testing.T) { //nolint:maintidx testData := []sampleBuilderTest{ { message: "SampleBuilder shouldn't emit anything if only one RTP packet has been pushed", @@ -76,6 +77,7 @@ func TestSampleBuilder(t *testing.T) { maxLateTimestamp: 0, }, { + //nolint:lll message: "SampleBuilder shouldn't emit anything if only one RTP packet has been pushed even if the market bit is set", packets: []*rtp.Packet{ {Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5, Marker: true}, Payload: []byte{0x01}}, @@ -243,14 +245,22 @@ func TestSampleBuilder(t *testing.T) { { message: "Sample builder should recognize padding packets", packets: []*rtp.Packet{ - {Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}}, // 1st packet - {Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1}, Payload: []byte{2}}, // 2nd packet - {Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1, Marker: true}, Payload: []byte{3}}, // 3rd packet - {Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}}, // Padding packet 1 - {Header: rtp.Header{SequenceNumber: 5004, Timestamp: 1}, Payload: []byte{}}, // Padding packet 2 - {Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, // 6th packet - {Header: rtp.Header{SequenceNumber: 5006, Timestamp: 3, Marker: true}, Payload: []byte{7}}, // 7th packet - {Header: rtp.Header{SequenceNumber: 5007, Timestamp: 4}, Payload: []byte{1}}, // 7th packet + // 1st packet + {Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}}, + // 2nd packet + {Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1}, Payload: []byte{2}}, + // 3rd packet + {Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1, Marker: true}, Payload: []byte{3}}, + // Padding packet 1 + {Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}}, + // Padding packet 2 + {Header: rtp.Header{SequenceNumber: 5004, Timestamp: 1}, Payload: []byte{}}, + // 6th packet + {Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, + // 7th packet + {Header: rtp.Header{SequenceNumber: 5006, Timestamp: 3, Marker: true}, Payload: []byte{7}}, + // 7th packet + {Header: rtp.Header{SequenceNumber: 5007, Timestamp: 4}, Payload: []byte{1}}, }, withHeadChecker: true, headBytes: []byte{1}, @@ -261,14 +271,21 @@ func TestSampleBuilder(t *testing.T) { maxLateTimestamp: 2000, }, { + //nolint:lll message: "Sample builder should build a sample out of a packet that's both start and end following a run of padding packets", packets: []*rtp.Packet{ - {Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}}, // 1st valid packet - {Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1, Marker: true}, Payload: []byte{2}}, // 2nd valid packet - {Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1}, Payload: []byte{}}, // 1st padding packet - {Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}}, // 2nd padding packet - {Header: rtp.Header{SequenceNumber: 5004, Timestamp: 2, Marker: true}, Payload: []byte{1}}, // 3rd valid packet - {Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, // 4th valid packet, start of next sample + // 1st valid packet + {Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}}, + // 2nd valid packet + {Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1, Marker: true}, Payload: []byte{2}}, + // 1st padding packet + {Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1}, Payload: []byte{}}, + // 2nd padding packet + {Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}}, + // 3rd valid packet + {Header: rtp.Header{SequenceNumber: 5004, Timestamp: 2, Marker: true}, Payload: []byte{1}}, + // 4th valid packet, start of next sample + {Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, }, withHeadChecker: true, headBytes: []byte{1}, @@ -304,55 +321,72 @@ func TestSampleBuilder(t *testing.T) { t.Run("Pop", func(t *testing.T) { assert := assert.New(t) - for _, t := range testData { + for _, td := range testData { var opts []Option - if t.maxLateTimestamp != 0 { + if td.maxLateTimestamp != 0 { opts = append(opts, WithMaxTimeDelay( - time.Millisecond*time.Duration(int64(t.maxLateTimestamp)), + time.Millisecond*time.Duration(int64(td.maxLateTimestamp)), )) } - if t.withRTPHeader { + if td.withRTPHeader { opts = append(opts, WithRTPHeaders(true)) } d := &fakeDepacketizer{ - headChecker: t.withHeadChecker, - headBytes: t.headBytes, + headChecker: td.withHeadChecker, + headBytes: td.headBytes, } - s := New(t.maxLate, d, 1, opts...) + s := New(td.maxLate, d, 1, opts...) samples := []*media.Sample{} - for _, p := range t.packets { + for _, p := range td.packets { s.Push(p) } for sample := s.Pop(); sample != nil; sample = s.Pop() { samples = append(samples, sample) } - assert.Equal(t.samples, samples, t.message) + assert.Equal(td.samples, samples, td.message) } }) } -// SampleBuilder should respect maxLate if we popped successfully but then have a gap larger then maxLate +// SampleBuilder should respect maxLate if we popped successfully but then have a gap larger then maxLate. func TestSampleBuilderMaxLate(t *testing.T) { assert := assert.New(t) - s := New(50, &fakeDepacketizer{}, 1) + fd := New(50, &fakeDepacketizer{}, 1) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 0, Timestamp: 1}, Payload: []byte{0x01}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1, Timestamp: 2}, Payload: []byte{0x01}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 2, Timestamp: 3}, Payload: []byte{0x01}}) - assert.Equal(&media.Sample{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 1}, s.Pop(), "Failed to build samples before gap") + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 0, Timestamp: 1}, Payload: []byte{0x01}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1, Timestamp: 2}, Payload: []byte{0x01}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 2, Timestamp: 3}, Payload: []byte{0x01}}) + assert.Equal(&media.Sample{ + Data: []byte{0x01}, + Duration: time.Second, + PacketTimestamp: 1, + }, fd.Pop(), "Failed to build samples before gap") - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}}) - assert.Equal(&media.Sample{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 2}, s.Pop(), "Failed to build samples after large gap") - assert.Equal((*media.Sample)(nil), s.Pop(), "Failed to build samples after large gap") + assert.Equal(&media.Sample{ + Data: []byte{0x01}, + Duration: time.Second, + PacketTimestamp: 2, + }, fd.Pop(), "Failed to build samples after large gap") + assert.Equal((*media.Sample)(nil), fd.Pop(), "Failed to build samples after large gap") - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 6000, Timestamp: 600}, Payload: []byte{0x03}}) - assert.Equal(&media.Sample{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 500, PrevDroppedPackets: 4998}, s.Pop(), "Failed to build samples after large gap") - assert.Equal(&media.Sample{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 501}, s.Pop(), "Failed to build samples after large gap") + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 6000, Timestamp: 600}, Payload: []byte{0x03}}) + assert.Equal(&media.Sample{ + Data: []byte{0x02}, + Duration: time.Second, + PacketTimestamp: 500, + PrevDroppedPackets: 4998, + }, fd.Pop(), "Failed to build samples after large gap") + assert.Equal(&media.Sample{ + Data: []byte{0x02}, + Duration: time.Second, + PacketTimestamp: 501, + }, fd.Pop(), "Failed to build samples after large gap") } func TestSeqnumDistance(t *testing.T) { @@ -385,25 +419,25 @@ func TestSampleBuilderCleanReference(t *testing.T) { } { seqStart := seqStart t.Run(fmt.Sprintf("From%d", seqStart), func(t *testing.T) { - s := New(10, &fakeDepacketizer{}, 1) + fd := New(10, &fakeDepacketizer{}, 1) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 0 + seqStart, Timestamp: 0}, Payload: []byte{0x01}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1 + seqStart, Timestamp: 0}, Payload: []byte{0x02}}) - s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 2 + seqStart, Timestamp: 0}, Payload: []byte{0x03}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 0 + seqStart, Timestamp: 0}, Payload: []byte{0x01}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1 + seqStart, Timestamp: 0}, Payload: []byte{0x02}}) + fd.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 2 + seqStart, Timestamp: 0}, Payload: []byte{0x03}}) pkt4 := &rtp.Packet{Header: rtp.Header{SequenceNumber: 14 + seqStart, Timestamp: 120}, Payload: []byte{0x04}} - s.Push(pkt4) + fd.Push(pkt4) pkt5 := &rtp.Packet{Header: rtp.Header{SequenceNumber: 12 + seqStart, Timestamp: 120}, Payload: []byte{0x05}} - s.Push(pkt5) + fd.Push(pkt5) for i := 0; i < 3; i++ { - if s.buffer[(i+int(seqStart))%0x10000] != nil { + if fd.buffer[(i+int(seqStart))%0x10000] != nil { t.Errorf("Old packet (%d) is not unreferenced (maxLate: 10, pushed: 12)", i) } } - if s.buffer[(14+int(seqStart))%0x10000] != pkt4 { + if fd.buffer[(14+int(seqStart))%0x10000] != pkt4 { t.Error("New packet must be referenced after jump") } - if s.buffer[(12+int(seqStart))%0x10000] != pkt5 { + if fd.buffer[(12+int(seqStart))%0x10000] != pkt5 { t.Error("New packet must be referenced after jump") } }) @@ -441,9 +475,9 @@ func TestSampleBuilderWithPacketReleaseHandler(t *testing.T) { {Header: rtp.Header{SequenceNumber: 13, Timestamp: 122}, Payload: []byte{0x04}}, {Header: rtp.Header{SequenceNumber: 21, Timestamp: 200}, Payload: []byte{0x05}}, } - s := New(10, &fakeDepacketizer{}, 1, WithPacketReleaseHandler(fakePacketReleaseHandler)) - s.Push(&pkts[0]) - s.Push(&pkts[1]) + fd := New(10, &fakeDepacketizer{}, 1, WithPacketReleaseHandler(fakePacketReleaseHandler)) + fd.Push(&pkts[0]) + fd.Push(&pkts[1]) if len(released) == 0 { t.Errorf("Old packet is not released") } @@ -451,10 +485,10 @@ func TestSampleBuilderWithPacketReleaseHandler(t *testing.T) { t.Errorf("Unexpected packet released by maxLate") } // Test packets released after samples built. - s.Push(&pkts[2]) - s.Push(&pkts[3]) - s.Push(&pkts[4]) - if s.Pop() == nil { + fd.Push(&pkts[2]) + fd.Push(&pkts[3]) + fd.Push(&pkts[4]) + if fd.Pop() == nil { t.Errorf("Should have some sample here.") } if len(released) < 3 { @@ -477,6 +511,7 @@ func TestSampleBuilderWithPacketHeadHandler(t *testing.T) { headCount := 0 s := New(10, &fakeDepacketizer{}, 1, WithPacketHeadHandler(func(interface{}) interface{} { headCount++ + return true })) @@ -498,37 +533,37 @@ func TestSampleBuilderWithPacketHeadHandler(t *testing.T) { } func TestSampleBuilderData(t *testing.T) { - s := New(10, &fakeDepacketizer{ + fd := New(10, &fakeDepacketizer{ headChecker: true, alwaysHead: true, }, 1) - j := 0 + validSamples := 0 for i := 0; i < 0x20000; i++ { - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i + 42), //nolint:gosec // G115 }, Payload: []byte{byte(i)}, } - s.Push(&p) + fd.Push(&packet) for { - sample := s.Pop() + sample := fd.Pop() if sample == nil { break } - assert.Equal(t, sample.PacketTimestamp, uint32(j+42), "timestamp") + assert.Equal(t, sample.PacketTimestamp, uint32(validSamples+42), "timestamp") //nolint:gosec // G115 assert.Equal(t, len(sample.Data), 1, "data length") - assert.Equal(t, byte(j), sample.Data[0], "data") - j++ + assert.Equal(t, byte(validSamples), sample.Data[0], "data") + validSamples++ } } // only the last packet should be dropped - assert.Equal(t, j, 0x1FFFF) + assert.Equal(t, validSamples, 0x1FFFF) } func TestSampleBuilderPacketUnreference(t *testing.T) { - s := New(10, &fakeDepacketizer{ + fd := New(10, &fakeDepacketizer{ headChecker: true, }, 1) @@ -539,17 +574,17 @@ func TestSampleBuilderPacketUnreference(t *testing.T) { for i := 0; i < 0x20000; i++ { atomic.AddInt64(&refs, 1) - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i + 42), //nolint:gosec // G115 }, Payload: []byte{byte(i)}, } - runtime.SetFinalizer(&p, finalizer) - s.Push(&p) + runtime.SetFinalizer(&packet, finalizer) + fd.Push(&packet) for { - sample := s.Pop() + sample := fd.Pop() if sample == nil { break } @@ -560,40 +595,40 @@ func TestSampleBuilderPacketUnreference(t *testing.T) { time.Sleep(10 * time.Millisecond) remainedRefs := atomic.LoadInt64(&refs) - runtime.KeepAlive(s) + runtime.KeepAlive(fd) // only the last packet should be still referenced assert.Equal(t, int64(1), remainedRefs) } func TestSampleBuilder_Flush(t *testing.T) { - s := New(50, &fakeDepacketizer{ + fd := New(50, &fakeDepacketizer{ headChecker: true, headBytes: []byte{0x01}, }, 1) - s.Push(&rtp.Packet{ + fd.Push(&rtp.Packet{ Header: rtp.Header{SequenceNumber: 999, Timestamp: 0}, Payload: []byte{0x00}, }) // Invalid packet // Gap preventing below packets to be processed - s.Push(&rtp.Packet{ + fd.Push(&rtp.Packet{ Header: rtp.Header{SequenceNumber: 1001, Timestamp: 1, Marker: true}, Payload: []byte{0x01, 0x11}, }) // Valid packet - s.Push(&rtp.Packet{ + fd.Push(&rtp.Packet{ Header: rtp.Header{SequenceNumber: 1011, Timestamp: 10, Marker: true}, Payload: []byte{0x01, 0x12}, }) // Valid packet - if sample := s.Pop(); sample != nil { + if sample := fd.Pop(); sample != nil { t.Fatal("Unexpected sample is returned. Test precondition may be broken") } - s.Flush() + fd.Flush() samples := []*media.Sample{} - for sample := s.Pop(); sample != nil; sample = s.Pop() { + for sample := fd.Pop(); sample != nil; sample = fd.Pop() { samples = append(samples, sample) } @@ -606,137 +641,137 @@ func TestSampleBuilder_Flush(t *testing.T) { } func BenchmarkSampleBuilderSequential(b *testing.B) { - s := New(100, &fakeDepacketizer{}, 1) + fd := New(100, &fakeDepacketizer{}, 1) b.ResetTimer() - j := 0 + validSamples := 0 for i := 0; i < b.N; i++ { - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i + 42), //nolint:gosec // G115 }, Payload: make([]byte, 50), } - s.Push(&p) + fd.Push(&packet) for { - s := s.Pop() + s := fd.Pop() if s == nil { break } - j++ + validSamples++ } } - if b.N > 200 && j < b.N-100 { - b.Errorf("Got %v (N=%v)", j, b.N) + if b.N > 200 && validSamples < b.N-100 { + b.Errorf("Got %v (N=%v)", validSamples, b.N) } } func BenchmarkSampleBuilderLoss(b *testing.B) { - s := New(100, &fakeDepacketizer{}, 1) + fd := New(100, &fakeDepacketizer{}, 1) b.ResetTimer() - j := 0 + validSamples := 0 for i := 0; i < b.N; i++ { if i%13 == 0 { continue } - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i + 42), //nolint:gosec // G115 }, Payload: make([]byte, 50), } - s.Push(&p) + fd.Push(&packet) for { - s := s.Pop() + s := fd.Pop() if s == nil { break } - j++ + validSamples++ } } - if b.N > 200 && j < b.N/2-100 { - b.Errorf("Got %v (N=%v)", j, b.N) + if b.N > 200 && validSamples < b.N/2-100 { + b.Errorf("Got %v (N=%v)", validSamples, b.N) } } func BenchmarkSampleBuilderReordered(b *testing.B) { - s := New(100, &fakeDepacketizer{}, 1) + fd := New(100, &fakeDepacketizer{}, 1) b.ResetTimer() - j := 0 + validSamples := 0 for i := 0; i < b.N; i++ { - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i ^ 3), - Timestamp: uint32((i ^ 3) + 42), + SequenceNumber: uint16(i ^ 3), //nolint:gosec // G115 + Timestamp: uint32((i ^ 3) + 42), //nolint:gosec // G115 }, Payload: make([]byte, 50), } - s.Push(&p) + fd.Push(&packet) for { - s := s.Pop() + s := fd.Pop() if s == nil { break } - j++ + validSamples++ } } - if b.N > 2 && j < b.N-5 && j > b.N { - b.Errorf("Got %v (N=%v)", j, b.N) + if b.N > 2 && validSamples < b.N-5 && validSamples > b.N { + b.Errorf("Got %v (N=%v)", validSamples, b.N) } } func BenchmarkSampleBuilderFragmented(b *testing.B) { - s := New(100, &fakeDepacketizer{}, 1) + fd := New(100, &fakeDepacketizer{}, 1) b.ResetTimer() - j := 0 + validSamples := 0 for i := 0; i < b.N; i++ { - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i/2 + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i/2 + 42), //nolint:gosec // G115 }, Payload: make([]byte, 50), } - s.Push(&p) + fd.Push(&packet) for { - s := s.Pop() + s := fd.Pop() if s == nil { break } - j++ + validSamples++ } } - if b.N > 200 && j < b.N/2-100 { - b.Errorf("Got %v (N=%v)", j, b.N) + if b.N > 200 && validSamples < b.N/2-100 { + b.Errorf("Got %v (N=%v)", validSamples, b.N) } } func BenchmarkSampleBuilderFragmentedLoss(b *testing.B) { - s := New(100, &fakeDepacketizer{}, 1) + fd := New(100, &fakeDepacketizer{}, 1) b.ResetTimer() - j := 0 + validSamples := 0 for i := 0; i < b.N; i++ { if i%13 == 0 { continue } - p := rtp.Packet{ + packet := rtp.Packet{ Header: rtp.Header{ - SequenceNumber: uint16(i), - Timestamp: uint32(i/2 + 42), + SequenceNumber: uint16(i), //nolint:gosec // G115 + Timestamp: uint32(i/2 + 42), //nolint:gosec // G115 }, Payload: make([]byte, 50), } - s.Push(&p) + fd.Push(&packet) for { - s := s.Pop() + s := fd.Pop() if s == nil { break } - j++ + validSamples++ } } - if b.N > 200 && j < b.N/3-100 { - b.Errorf("Got %v (N=%v)", j, b.N) + if b.N > 200 && validSamples < b.N/3-100 { + b.Errorf("Got %v (N=%v)", validSamples, b.N) } } diff --git a/pkg/null/null.go b/pkg/null/null.go index 0ad14d81..5e775d6c 100644 --- a/pkg/null/null.go +++ b/pkg/null/null.go @@ -5,200 +5,200 @@ // This pattern is common in ECMAScript, this allows us to maintain a matching API package null -// Bool is used to represent a bool that may be null +// Bool is used to represent a bool that may be null. type Bool struct { Valid bool Bool bool } -// NewBool turns a bool into a valid null.Bool +// NewBool turns a bool into a valid null.Bool. func NewBool(value bool) Bool { return Bool{Valid: true, Bool: value} } -// Byte is used to represent a byte that may be null +// Byte is used to represent a byte that may be null. type Byte struct { Valid bool Byte byte } -// NewByte turns a byte into a valid null.Byte +// NewByte turns a byte into a valid null.Byte. func NewByte(value byte) Byte { return Byte{Valid: true, Byte: value} } -// Complex128 is used to represent a complex128 that may be null +// Complex128 is used to represent a complex128 that may be null. type Complex128 struct { Valid bool Complex128 complex128 } -// NewComplex128 turns a complex128 into a valid null.Complex128 +// NewComplex128 turns a complex128 into a valid null.Complex128. func NewComplex128(value complex128) Complex128 { return Complex128{Valid: true, Complex128: value} } -// Complex64 is used to represent a complex64 that may be null +// Complex64 is used to represent a complex64 that may be null. type Complex64 struct { Valid bool Complex64 complex64 } -// NewComplex64 turns a complex64 into a valid null.Complex64 +// NewComplex64 turns a complex64 into a valid null.Complex64. func NewComplex64(value complex64) Complex64 { return Complex64{Valid: true, Complex64: value} } -// Float32 is used to represent a float32 that may be null +// Float32 is used to represent a float32 that may be null. type Float32 struct { Valid bool Float32 float32 } -// NewFloat32 turns a float32 into a valid null.Float32 +// NewFloat32 turns a float32 into a valid null.Float32. func NewFloat32(value float32) Float32 { return Float32{Valid: true, Float32: value} } -// Float64 is used to represent a float64 that may be null +// Float64 is used to represent a float64 that may be null. type Float64 struct { Valid bool Float64 float64 } -// NewFloat64 turns a float64 into a valid null.Float64 +// NewFloat64 turns a float64 into a valid null.Float64. func NewFloat64(value float64) Float64 { return Float64{Valid: true, Float64: value} } -// Int is used to represent a int that may be null +// Int is used to represent a int that may be null. type Int struct { Valid bool Int int } -// NewInt turns a int into a valid null.Int +// NewInt turns a int into a valid null.Int. func NewInt(value int) Int { return Int{Valid: true, Int: value} } -// Int16 is used to represent a int16 that may be null +// Int16 is used to represent a int16 that may be null. type Int16 struct { Valid bool Int16 int16 } -// NewInt16 turns a int16 into a valid null.Int16 +// NewInt16 turns a int16 into a valid null.Int16. func NewInt16(value int16) Int16 { return Int16{Valid: true, Int16: value} } -// Int32 is used to represent a int32 that may be null +// Int32 is used to represent a int32 that may be null. type Int32 struct { Valid bool Int32 int32 } -// NewInt32 turns a int32 into a valid null.Int32 +// NewInt32 turns a int32 into a valid null.Int32. func NewInt32(value int32) Int32 { return Int32{Valid: true, Int32: value} } -// Int64 is used to represent a int64 that may be null +// Int64 is used to represent a int64 that may be null. type Int64 struct { Valid bool Int64 int64 } -// NewInt64 turns a int64 into a valid null.Int64 +// NewInt64 turns a int64 into a valid null.Int64. func NewInt64(value int64) Int64 { return Int64{Valid: true, Int64: value} } -// Int8 is used to represent a int8 that may be null +// Int8 is used to represent a int8 that may be null. type Int8 struct { Valid bool Int8 int8 } -// NewInt8 turns a int8 into a valid null.Int8 +// NewInt8 turns a int8 into a valid null.Int8. func NewInt8(value int8) Int8 { return Int8{Valid: true, Int8: value} } -// Rune is used to represent a rune that may be null +// Rune is used to represent a rune that may be null. type Rune struct { Valid bool Rune rune } -// NewRune turns a rune into a valid null.Rune +// NewRune turns a rune into a valid null.Rune. func NewRune(value rune) Rune { return Rune{Valid: true, Rune: value} } -// String is used to represent a string that may be null +// String is used to represent a string that may be null. type String struct { Valid bool String string } -// NewString turns a string into a valid null.String +// NewString turns a string into a valid null.String. func NewString(value string) String { return String{Valid: true, String: value} } -// Uint is used to represent a uint that may be null +// Uint is used to represent a uint that may be null. type Uint struct { Valid bool Uint uint } -// NewUint turns a uint into a valid null.Uint +// NewUint turns a uint into a valid null.Uint. func NewUint(value uint) Uint { return Uint{Valid: true, Uint: value} } -// Uint16 is used to represent a uint16 that may be null +// Uint16 is used to represent a uint16 that may be null. type Uint16 struct { Valid bool Uint16 uint16 } -// NewUint16 turns a uint16 into a valid null.Uint16 +// NewUint16 turns a uint16 into a valid null.Uint16. func NewUint16(value uint16) Uint16 { return Uint16{Valid: true, Uint16: value} } -// Uint32 is used to represent a uint32 that may be null +// Uint32 is used to represent a uint32 that may be null. type Uint32 struct { Valid bool Uint32 uint32 } -// NewUint32 turns a uint32 into a valid null.Uint32 +// NewUint32 turns a uint32 into a valid null.Uint32. func NewUint32(value uint32) Uint32 { return Uint32{Valid: true, Uint32: value} } -// Uint64 is used to represent a uint64 that may be null +// Uint64 is used to represent a uint64 that may be null. type Uint64 struct { Valid bool Uint64 uint64 } -// NewUint64 turns a uint64 into a valid null.Uint64 +// NewUint64 turns a uint64 into a valid null.Uint64. func NewUint64(value uint64) Uint64 { return Uint64{Valid: true, Uint64: value} } -// Uint8 is used to represent a uint8 that may be null +// Uint8 is used to represent a uint8 that may be null. type Uint8 struct { Valid bool Uint8 uint8 } -// NewUint8 turns a uint8 into a valid null.Uint8 +// NewUint8 turns a uint8 into a valid null.Uint8. func NewUint8(value uint8) Uint8 { return Uint8{Valid: true, Uint8: value} } diff --git a/rtcpmuxpolicy.go b/rtcpmuxpolicy.go index ec84ea91..d4f7f476 100644 --- a/rtcpmuxpolicy.go +++ b/rtcpmuxpolicy.go @@ -12,7 +12,7 @@ import ( type RTCPMuxPolicy int const ( - // RTCPMuxPolicyUnknown is the enum's zero-value + // RTCPMuxPolicyUnknown is the enum's zero-value. RTCPMuxPolicyUnknown RTCPMuxPolicy = iota // RTCPMuxPolicyNegotiate indicates to gather ICE candidates for both @@ -55,7 +55,7 @@ func (t RTCPMuxPolicy) String() string { } } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (t *RTCPMuxPolicy) UnmarshalJSON(b []byte) error { var val string if err := json.Unmarshal(b, &val); err != nil { @@ -63,10 +63,11 @@ func (t *RTCPMuxPolicy) UnmarshalJSON(b []byte) error { } *t = newRTCPMuxPolicy(val) + return nil } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (t RTCPMuxPolicy) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } diff --git a/rtpcodec.go b/rtpcodec.go index a414d510..cc079280 100644 --- a/rtpcodec.go +++ b/rtpcodec.go @@ -10,17 +10,17 @@ import ( "github.com/pion/webrtc/v4/internal/fmtp" ) -// RTPCodecType determines the type of a codec +// RTPCodecType determines the type of a codec. type RTPCodecType int const ( - // RTPCodecTypeUnknown is the enum's zero-value + // RTPCodecTypeUnknown is the enum's zero-value. RTPCodecTypeUnknown RTPCodecType = iota - // RTPCodecTypeAudio indicates this is an audio codec + // RTPCodecTypeAudio indicates this is an audio codec. RTPCodecTypeAudio - // RTPCodecTypeVideo indicates this is a video codec + // RTPCodecTypeVideo indicates this is a video codec. RTPCodecTypeVideo ) @@ -35,7 +35,7 @@ func (t RTPCodecType) String() string { } } -// NewRTPCodecType creates a RTPCodecType from a string +// NewRTPCodecType creates a RTPCodecType from a string. func NewRTPCodecType(r string) RTPCodecType { switch { case strings.EqualFold(r, RTPCodecTypeAudio.String()): @@ -103,8 +103,11 @@ const ( // Do a fuzzy find for a codec in the list of codecs // Used for lookup up a codec in an existing list to find a match -// Returns codecMatchExact, codecMatchPartial, or codecMatchNone -func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecParameters) (RTPCodecParameters, codecMatchType) { +// Returns codecMatchExact, codecMatchPartial, or codecMatchNone. +func codecParametersFuzzySearch( + needle RTPCodecParameters, + haystack []RTPCodecParameters, +) (RTPCodecParameters, codecMatchType) { needleFmtp := fmtp.Parse(needle.RTPCodecCapability.MimeType, needle.RTPCodecCapability.SDPFmtpLine) // First attempt to match on MimeType + SDPFmtpLine @@ -125,7 +128,7 @@ func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecPa return RTPCodecParameters{}, codecMatchNone } -// Given a CodecParameters find the RTX CodecParameters if one exists +// Given a CodecParameters find the RTX CodecParameters if one exists. func findRTXPayloadType(needle PayloadType, haystack []RTPCodecParameters) PayloadType { aptStr := fmt.Sprintf("apt=%d", needle) for _, c := range haystack { @@ -142,6 +145,7 @@ func rtcpFeedbackIntersection(a, b []RTCPFeedback) (out []RTCPFeedback) { for _, bFeeback := range b { if aFeedback.Type == bFeeback.Type && aFeedback.Parameter == bFeeback.Parameter { out = append(out, aFeedback) + break } } diff --git a/rtpreceiveparameters.go b/rtpreceiveparameters.go index 26a66761..a07bbc6c 100644 --- a/rtpreceiveparameters.go +++ b/rtpreceiveparameters.go @@ -3,7 +3,7 @@ package webrtc -// RTPReceiveParameters contains the RTP stack settings used by receivers +// RTPReceiveParameters contains the RTP stack settings used by receivers. type RTPReceiveParameters struct { Encodings []RTPDecodingParameters } diff --git a/rtpreceiver.go b/rtpreceiver.go index ba1afb55..b4757636 100644 --- a/rtpreceiver.go +++ b/rtpreceiver.go @@ -20,7 +20,7 @@ import ( ) // trackStreams maintains a mapping of RTP/RTCP streams to a specific track -// a RTPReceiver may contain multiple streams if we are dealing with Simulcast +// a RTPReceiver may contain multiple streams if we are dealing with Simulcast. type trackStreams struct { track *TrackRemote @@ -54,7 +54,7 @@ func (p *rtxPacketWithAttributes) release() { } } -// RTPReceiver allows an application to inspect the receipt of a TrackRemote +// RTPReceiver allows an application to inspect the receipt of a TrackRemote. type RTPReceiver struct { kind RTPCodecType transport *DTLSTransport @@ -72,7 +72,7 @@ type RTPReceiver struct { rtxPool sync.Pool } -// NewRTPReceiver constructs a new RTPReceiver +// NewRTPReceiver constructs a new RTPReceiver. func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTransport) (*RTPReceiver, error) { if transport == nil { return nil, errRTPReceiverDTLSTransportNil @@ -100,18 +100,23 @@ func (r *RTPReceiver) setRTPTransceiver(tr *RTPTransceiver) { } // Transport returns the currently-configured *DTLSTransport or nil -// if one has not yet been configured +// if one has not yet been configured. func (r *RTPReceiver) Transport() *DTLSTransport { r.mu.RLock() defer r.mu.RUnlock() + return r.transport } func (r *RTPReceiver) getParameters() RTPParameters { - parameters := r.api.mediaEngine.getRTPParametersByKind(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}) + parameters := r.api.mediaEngine.getRTPParametersByKind( + r.kind, + []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}, + ) if r.tr != nil { parameters.Codecs = r.tr.getCodecs() } + return parameters } @@ -120,10 +125,11 @@ func (r *RTPReceiver) getParameters() RTPParameters { func (r *RTPReceiver) GetParameters() RTPParameters { r.mu.RLock() defer r.mu.RUnlock() + return r.getParameters() } -// Track returns the RtpTransceiver TrackRemote +// Track returns the RtpTransceiver TrackRemote. func (r *RTPReceiver) Track() *TrackRemote { r.mu.RLock() defer r.mu.RUnlock() @@ -131,11 +137,12 @@ func (r *RTPReceiver) Track() *TrackRemote { if len(r.tracks) != 1 { return nil } + return r.tracks[0].track } // Tracks returns the RtpTransceiver tracks -// A RTPReceiver to support Simulcast may now have multiple tracks +// A RTPReceiver to support Simulcast may now have multiple tracks. func (r *RTPReceiver) Tracks() []*TrackRemote { r.mu.RLock() defer r.mu.RUnlock() @@ -144,11 +151,12 @@ func (r *RTPReceiver) Tracks() []*TrackRemote { for i := range r.tracks { tracks = append(tracks, r.tracks[i].track) } + return tracks } // RTPTransceiver returns the RTPTransceiver this -// RTPReceiver belongs too, or nil if none +// RTPReceiver belongs too, or nil if none. func (r *RTPReceiver) RTPTransceiver() *RTPTransceiver { r.mu.Lock() defer r.mu.Unlock() @@ -156,7 +164,7 @@ func (r *RTPReceiver) RTPTransceiver() *RTPTransceiver { return r.tr } -// configureReceive initialize the track +// configureReceive initialize the track. func (r *RTPReceiver) configureReceive(parameters RTPReceiveParameters) { r.mu.Lock() defer r.mu.Unlock() @@ -176,8 +184,8 @@ func (r *RTPReceiver) configureReceive(parameters RTPReceiveParameters) { } } -// startReceive starts all the transports -func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { +// startReceive starts all the transports. +func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { //nolint:cyclop r.mu.Lock() defer r.mu.Unlock() select { @@ -199,31 +207,51 @@ func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { continue } - var t *trackStreams + var streams *trackStreams for idx, ts := range r.tracks { if ts.track != nil && ts.track.SSRC() == parameters.Encodings[i].SSRC { - t = &r.tracks[idx] + streams = &r.tracks[idx] + break } } - if t == nil { + if streams == nil { return fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, parameters.Encodings[i].SSRC) } - t.streamInfo = createStreamInfo("", parameters.Encodings[i].SSRC, 0, 0, 0, 0, 0, codec, globalParams.HeaderExtensions) + streams.streamInfo = createStreamInfo( + "", + parameters.Encodings[i].SSRC, + 0, 0, 0, 0, 0, + codec, + globalParams.HeaderExtensions, + ) var err error - if t.rtpReadStream, t.rtpInterceptor, t.rtcpReadStream, t.rtcpInterceptor, err = r.transport.streamsForSSRC(parameters.Encodings[i].SSRC, *t.streamInfo); err != nil { + + //nolint:lll // # TODO refactor + if streams.rtpReadStream, streams.rtpInterceptor, streams.rtcpReadStream, streams.rtcpInterceptor, err = r.transport.streamsForSSRC(parameters.Encodings[i].SSRC, *streams.streamInfo); err != nil { return err } if rtxSsrc := parameters.Encodings[i].RTX.SSRC; rtxSsrc != 0 { streamInfo := createStreamInfo("", rtxSsrc, 0, 0, 0, 0, 0, codec, globalParams.HeaderExtensions) - rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor, err := r.transport.streamsForSSRC(rtxSsrc, *streamInfo) + rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor, err := r.transport.streamsForSSRC( + rtxSsrc, + *streamInfo, + ) if err != nil { return err } - if err = r.receiveForRtx(rtxSsrc, "", streamInfo, rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor); err != nil { + if err = r.receiveForRtx( + rtxSsrc, + "", + streamInfo, + rtpReadStream, + rtpInterceptor, + rtcpReadStream, + rtcpInterceptor, + ); err != nil { return err } } @@ -232,13 +260,14 @@ func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) error { return nil } -// Receive initialize the track and starts all the transports +// Receive initialize the track and starts all the transports. func (r *RTPReceiver) Receive(parameters RTPReceiveParameters) error { r.configureReceive(parameters) + return r.startReceive(parameters) } -// Read reads incoming RTCP for this RTPReceiver +// Read reads incoming RTCP for this RTPReceiver. func (r *RTPReceiver) Read(b []byte) (n int, a interceptor.Attributes, err error) { select { case <-r.received: @@ -248,7 +277,7 @@ func (r *RTPReceiver) Read(b []byte) (n int, a interceptor.Attributes, err error } } -// ReadSimulcast reads incoming RTCP for this RTPReceiver for given rid +// ReadSimulcast reads incoming RTCP for this RTPReceiver for given rid. func (r *RTPReceiver) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) { select { case <-r.received: @@ -265,6 +294,7 @@ func (r *RTPReceiver) ReadSimulcast(b []byte, rid string) (n int, a interceptor. if rtcpInterceptor == nil { return 0, nil, fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid) } + return rtcpInterceptor.Read(b, a) case <-r.closed: @@ -289,7 +319,7 @@ func (r *RTPReceiver) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) return pkts, attributes, nil } -// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you +// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you. func (r *RTPReceiver) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) { b := make([]byte, r.api.settingEngine.getReceiveMTU()) i, attributes, err := r.ReadSimulcast(b, rid) @@ -298,6 +328,7 @@ func (r *RTPReceiver) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor. } pkts, err := rtcp.Unmarshal(b[:i]) + return pkts, attributes, err } @@ -310,8 +341,8 @@ func (r *RTPReceiver) haveReceived() bool { } } -// Stop irreversibly stops the RTPReceiver -func (r *RTPReceiver) Stop() error { +// Stop irreversibly stops the RTPReceiver. +func (r *RTPReceiver) Stop() error { //nolint:cyclop r.mu.Lock() defer r.mu.Unlock() var err error @@ -357,6 +388,7 @@ func (r *RTPReceiver) Stop() error { } close(r.closed) + return err } @@ -366,10 +398,11 @@ func (r *RTPReceiver) streamsForTrack(t *TrackRemote) *trackStreams { return &r.tracks[i] } } + return nil } -// readRTP should only be called by a track, this only exists so we can keep state in one place +// readRTP should only be called by a track, this only exists so we can keep state in one place. func (r *RTPReceiver) readRTP(b []byte, reader *TrackRemote) (n int, a interceptor.Attributes, err error) { <-r.received if t := r.streamsForTrack(reader); t != nil { @@ -380,8 +413,16 @@ func (r *RTPReceiver) readRTP(b []byte, reader *TrackRemote) (n int, a intercept } // receiveForRid is the sibling of Receive expect for RIDs instead of SSRCs -// It populates all the internal state for the given RID -func (r *RTPReceiver) receiveForRid(rid string, params RTPParameters, streamInfo *interceptor.StreamInfo, rtpReadStream *srtp.ReadStreamSRTP, rtpInterceptor interceptor.RTPReader, rtcpReadStream *srtp.ReadStreamSRTCP, rtcpInterceptor interceptor.RTCPReader) (*TrackRemote, error) { +// It populates all the internal state for the given RID. +func (r *RTPReceiver) receiveForRid( + rid string, + params RTPParameters, + streamInfo *interceptor.StreamInfo, + rtpReadStream *srtp.ReadStreamSRTP, + rtpInterceptor interceptor.RTPReader, + rtcpReadStream *srtp.ReadStreamSRTCP, + rtcpInterceptor interceptor.RTCPReader, +) (*TrackRemote, error) { r.mu.Lock() defer r.mu.Unlock() @@ -407,8 +448,18 @@ func (r *RTPReceiver) receiveForRid(rid string, params RTPParameters, streamInfo return nil, fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid) } -// receiveForRtx starts a routine that processes the repair stream -func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *interceptor.StreamInfo, rtpReadStream *srtp.ReadStreamSRTP, rtpInterceptor interceptor.RTPReader, rtcpReadStream *srtp.ReadStreamSRTCP, rtcpInterceptor interceptor.RTCPReader) error { +// receiveForRtx starts a routine that processes the repair stream. +// +//nolint:cyclop +func (r *RTPReceiver) receiveForRtx( + ssrc SSRC, + rsid string, + streamInfo *interceptor.StreamInfo, + rtpReadStream *srtp.ReadStreamSRTP, + rtpInterceptor interceptor.RTPReader, + rtcpReadStream *srtp.ReadStreamSRTCP, + rtcpInterceptor interceptor.RTCPReader, +) error { var track *trackStreams if ssrc != 0 && len(r.tracks) == 1 { track = &r.tracks[0] @@ -419,6 +470,7 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep if track.track.RtxSSRC() == 0 { track.track.setRtxSSRC(SSRC(streamInfo.SSRC)) } + break } } @@ -441,6 +493,7 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep i, attributes, err := track.repairInterceptor.Read(b, nil) if err != nil { r.rtxPool.Put(b) // nolint:staticcheck + return } @@ -462,6 +515,7 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep if i-int(headerLength)-paddingLength < 2 { // BWE probe packet, ignore r.rtxPool.Put(b) // nolint:staticcheck + continue } @@ -481,6 +535,7 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep select { case <-r.closed: r.rtxPool.Put(b) // nolint:staticcheck + return case track.repairStreamChannel <- rtxPacketWithAttributes{pkt: b[:i-2], attributes: attributes, pool: &r.rtxPool}: default: @@ -488,6 +543,7 @@ func (r *RTPReceiver) receiveForRtx(ssrc SSRC, rsid string, streamInfo *intercep } } }() + return nil } @@ -499,7 +555,8 @@ func (r *RTPReceiver) SetReadDeadline(t time.Time) error { return r.tracks[0].rtcpReadStream.SetReadDeadline(t) } -// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever. +// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. +// 0 is forever. func (r *RTPReceiver) SetReadDeadlineSimulcast(deadline time.Time, rid string) error { r.mu.RLock() defer r.mu.RUnlock() @@ -509,11 +566,12 @@ func (r *RTPReceiver) SetReadDeadlineSimulcast(deadline time.Time, rid string) e return t.rtcpReadStream.SetReadDeadline(deadline) } } + return fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid) } // setRTPReadDeadline sets the max amount of time the RTP stream will block before returning. 0 is forever. -// This should be fired by calling SetReadDeadline on the TrackRemote +// This should be fired by calling SetReadDeadline on the TrackRemote. func (r *RTPReceiver) setRTPReadDeadline(deadline time.Time, reader *TrackRemote) error { r.mu.RLock() defer r.mu.RUnlock() @@ -521,10 +579,11 @@ func (r *RTPReceiver) setRTPReadDeadline(deadline time.Time, reader *TrackRemote if t := r.streamsForTrack(reader); t != nil { return t.rtpReadStream.SetReadDeadline(deadline) } + return fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, reader.SSRC()) } -// readRTX returns an RTX packet if one is available on the RTX track, otherwise returns nil +// readRTX returns an RTX packet if one is available on the RTX track, otherwise returns nil. func (r *RTPReceiver) readRTX(reader *TrackRemote) *rtxPacketWithAttributes { if !reader.HasRTX() { return nil @@ -543,5 +602,6 @@ func (r *RTPReceiver) readRTX(reader *TrackRemote) *rtxPacketWithAttributes { default: } } + return nil } diff --git a/rtpreceiver_go_test.go b/rtpreceiver_go_test.go index d9f8eb85..2b7ff6de 100644 --- a/rtpreceiver_go_test.go +++ b/rtpreceiver_go_test.go @@ -27,11 +27,15 @@ func TestSetRTPParameters(t *testing.T) { // Those parameters wouldn't make sense in a real application, // but for the sake of the test we just need different values. - p := RTPParameters{ + params := RTPParameters{ Codecs: []RTPCodecParameters{ { - RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", []RTCPFeedback{{"nack", ""}}}, - PayloadType: 111, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeOpus, 48000, 2, + "minptime=10;useinbandfec=1", + []RTCPFeedback{{"nack", ""}}, + }, + PayloadType: 111, }, }, HeaderExtensions: []RTPHeaderExtensionParameter{ @@ -43,18 +47,18 @@ func TestSetRTPParameters(t *testing.T) { seenPacket, seenPacketCancel := context.WithCancel(context.Background()) receiver.OnTrack(func(_ *TrackRemote, r *RTPReceiver) { - r.SetRTPParameters(p) + r.SetRTPParameters(params) incomingTrackCodecs := r.Track().Codec() - assert.EqualValues(t, p.HeaderExtensions, r.Track().params.HeaderExtensions) + assert.EqualValues(t, params.HeaderExtensions, r.Track().params.HeaderExtensions) - assert.EqualValues(t, p.Codecs[0].MimeType, incomingTrackCodecs.MimeType) - assert.EqualValues(t, p.Codecs[0].ClockRate, incomingTrackCodecs.ClockRate) - assert.EqualValues(t, p.Codecs[0].Channels, incomingTrackCodecs.Channels) - assert.EqualValues(t, p.Codecs[0].SDPFmtpLine, incomingTrackCodecs.SDPFmtpLine) - assert.EqualValues(t, p.Codecs[0].RTCPFeedback, incomingTrackCodecs.RTCPFeedback) - assert.EqualValues(t, p.Codecs[0].PayloadType, incomingTrackCodecs.PayloadType) + assert.EqualValues(t, params.Codecs[0].MimeType, incomingTrackCodecs.MimeType) + assert.EqualValues(t, params.Codecs[0].ClockRate, incomingTrackCodecs.ClockRate) + assert.EqualValues(t, params.Codecs[0].Channels, incomingTrackCodecs.Channels) + assert.EqualValues(t, params.Codecs[0].SDPFmtpLine, incomingTrackCodecs.SDPFmtpLine) + assert.EqualValues(t, params.Codecs[0].RTCPFeedback, incomingTrackCodecs.RTCPFeedback) + assert.EqualValues(t, params.Codecs[0].PayloadType, incomingTrackCodecs.PayloadType) seenPacketCancel() }) diff --git a/rtpreceiver_test.go b/rtpreceiver_test.go index 40c7900d..0c09ca92 100644 --- a/rtpreceiver_test.go +++ b/rtpreceiver_test.go @@ -18,7 +18,7 @@ import ( ) // Assert that SetReadDeadline works as expected -// This test uses VNet since we must have zero loss +// This test uses VNet since we must have zero loss. func Test_RTPReceiver_SetReadDeadline(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() diff --git a/rtpsender.go b/rtpsender.go index 3a597619..20522741 100644 --- a/rtpsender.go +++ b/rtpsender.go @@ -32,7 +32,7 @@ type trackEncoding struct { ssrc, ssrcRTX, ssrcFEC SSRC } -// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer +// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer. type RTPSender struct { trackEncodings []*trackEncoding @@ -57,7 +57,7 @@ type RTPSender struct { sendCalled, stopCalled chan struct{} } -// NewRTPSender constructs a new RTPSender +// NewRTPSender constructs a new RTPSender. func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSender, error) { if track == nil { return nil, errRTPSenderTrackNil @@ -87,6 +87,7 @@ func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSe func (r *RTPSender) isNegotiated() bool { r.mu.RLock() defer r.mu.RUnlock() + return r.negotiated } @@ -103,10 +104,11 @@ func (r *RTPSender) setRTPTransceiver(rtpTransceiver *RTPTransceiver) { } // Transport returns the currently-configured *DTLSTransport or nil -// if one has not yet been configured +// if one has not yet been configured. func (r *RTPSender) Transport() *DTLSTransport { r.mu.RLock() defer r.mu.RUnlock() + return r.transport } @@ -144,11 +146,12 @@ func (r *RTPSender) GetParameters() RTPSendParameters { } else { sendParameters.Codecs = r.api.mediaEngine.getCodecsByKind(r.kind) } + return sendParameters } // AddEncoding adds an encoding to RTPSender. Used by simulcast senders. -func (r *RTPSender) AddEncoding(track TrackLocal) error { +func (r *RTPSender) AddEncoding(track TrackLocal) error { //nolint:cyclop r.mu.Lock() defer r.mu.Unlock() @@ -191,6 +194,7 @@ func (r *RTPSender) AddEncoding(track TrackLocal) error { } r.addEncoding(track) + return nil } @@ -211,7 +215,7 @@ func (r *RTPSender) addEncoding(track TrackLocal) { r.trackEncodings = append(r.trackEncodings, trackEncoding) } -// Track returns the RTCRtpTransceiver track, or nil +// Track returns the RTCRtpTransceiver track, or nil. func (r *RTPSender) Track() TrackLocal { r.mu.RLock() defer r.mu.RUnlock() @@ -226,7 +230,7 @@ func (r *RTPSender) Track() TrackLocal { // ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal. // The new track must be of the same media kind (audio, video, etc) and switching the track should not // require negotiation. -func (r *RTPSender) ReplaceTrack(track TrackLocal) error { +func (r *RTPSender) ReplaceTrack(track TrackLocal) error { //nolint:cyclop r.mu.Lock() defer r.mu.Unlock() @@ -260,10 +264,15 @@ func (r *RTPSender) ReplaceTrack(track TrackLocal) error { return nil } + params := r.api.mediaEngine.getRTPParametersByKind( + track.Kind(), + []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, + ) + // If we reach this point in the routine, there is only 1 track encoding codec, err := track.Bind(&baseTrackLocalContext{ id: context.ID(), - params: r.api.mediaEngine.getRTPParametersByKind(track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}), + params: params, ssrc: context.SSRC(), ssrcRTX: context.SSRCRetransmission(), ssrcFEC: context.SSRCForwardErrorCorrection(), @@ -305,17 +314,23 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error { trackEncoding := r.trackEncodings[idx] srtpStream := &srtpWriterFuture{ssrc: parameters.Encodings[idx].SSRC, rtpSender: r} writeStream := &interceptorToTrackLocalWriter{} - rtpParameters := r.api.mediaEngine.getRTPParametersByKind(trackEncoding.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}) + rtpParameters := r.api.mediaEngine.getRTPParametersByKind( + trackEncoding.track.Kind(), + []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, + ) trackEncoding.srtpStream = srtpStream trackEncoding.ssrc = parameters.Encodings[idx].SSRC trackEncoding.ssrcRTX = parameters.Encodings[idx].RTX.SSRC trackEncoding.ssrcFEC = parameters.Encodings[idx].FEC.SSRC trackEncoding.rtcpInterceptor = r.api.interceptor.BindRTCPReader( - interceptor.RTCPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { - n, err = trackEncoding.srtpStream.Read(in) - return n, a, err - }), + interceptor.RTCPReaderFunc( + func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { + n, err = trackEncoding.srtpStream.Read(in) + + return n, a, err + }, + ), ) trackEncoding.context = &baseTrackLocalContext{ id: r.id, @@ -356,15 +371,17 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error { } close(r.sendCalled) + return nil } -// Stop irreversibly stops the RTPSender +// Stop irreversibly stops the RTPSender. func (r *RTPSender) Stop() error { r.mu.Lock() if stopped := r.hasStopped(); stopped { r.mu.Unlock() + return nil } @@ -390,7 +407,7 @@ func (r *RTPSender) Stop() error { return util.FlattenErrs(errs) } -// Read reads incoming RTCP for this RTPSender +// Read reads incoming RTCP for this RTPSender. func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) { select { case <-r.sendCalled: @@ -416,7 +433,7 @@ func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) { return pkts, attributes, nil } -// ReadSimulcast reads incoming RTCP for this RTPSender for given rid +// ReadSimulcast reads incoming RTCP for this RTPSender for given rid. func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) { select { case <-r.sendCalled: @@ -425,13 +442,14 @@ func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.At return t.rtcpInterceptor.Read(b, a) } } + return 0, nil, fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid) case <-r.stopCalled: return 0, nil, io.ErrClosedPipe } } -// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you +// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you. func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) { b := make([]byte, r.api.settingEngine.getReceiveMTU()) i, attributes, err := r.ReadSimulcast(b, rid) @@ -440,6 +458,7 @@ func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.At } pkts, err := rtcp.Unmarshal(b[:i]) + return pkts, attributes, err } @@ -449,7 +468,8 @@ func (r *RTPSender) SetReadDeadline(t time.Time) error { return r.trackEncodings[0].srtpStream.SetReadDeadline(t) } -// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever. +// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid +// will block before returning. 0 is forever. func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid string) error { r.mu.RLock() defer r.mu.RUnlock() @@ -459,10 +479,11 @@ func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid string) err return t.srtpStream.SetReadDeadline(deadline) } } + return fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid) } -// hasSent tells if data has been ever sent for this instance +// hasSent tells if data has been ever sent for this instance. func (r *RTPSender) hasSent() bool { select { case <-r.sendCalled: @@ -472,7 +493,7 @@ func (r *RTPSender) hasSent() bool { } } -// hasStopped tells if stop has been called +// hasStopped tells if stop has been called. func (r *RTPSender) hasStopped() bool { select { case <-r.stopCalled: @@ -483,7 +504,7 @@ func (r *RTPSender) hasStopped() bool { } // Set a SSRC for FEC and RTX if MediaEngine has them enabled -// If the remote doesn't support FEC or RTX we disable locally +// If the remote doesn't support FEC or RTX we disable locally. func (r *RTPSender) configureRTXAndFEC() { r.mu.RLock() defer r.mu.RUnlock() diff --git a/rtpsender_test.go b/rtpsender_test.go index 458e7e87..623b5ab5 100644 --- a/rtpsender_test.go +++ b/rtpsender_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_RTPSender_ReplaceTrack(t *testing.T) { +func Test_RTPSender_ReplaceTrack(t *testing.T) { //nolint:cyclop lim := test.TimeOut(time.Second * 10) defer lim.Stop() @@ -53,6 +53,7 @@ func Test_RTPSender_ReplaceTrack(t *testing.T) { pkt, _, err := track.ReadRTP() if err != nil { assert.True(t, errors.Is(err, io.EOF)) + return } @@ -139,7 +140,9 @@ func Test_RTPSender_GetParameters_WithRID(t *testing.T) { assert.NoError(t, signalPair(offerer, answerer)) - track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("moo")) + track, err := NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("moo"), + ) assert.NoError(t, err) err = rtpTransceiver.setSendingTrack(track) @@ -358,40 +361,56 @@ func Test_RTPSender_Add_Encoding(t *testing.T) { assert.NoError(t, err) assert.Equal(t, errRTPSenderRidNil, rtpSender.AddEncoding(track1)) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderNoBaseEncoding, rtpSender.AddEncoding(track1)) - track, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q")) + track, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q"), + ) assert.NoError(t, err) rtpSender, err = peerConnection.AddTrack(track) assert.NoError(t, err) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video1", "pion", WithRTPStreamID("h")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video1", "pion", WithRTPStreamID("h"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1)) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1", WithRTPStreamID("h")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion1", WithRTPStreamID("h"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1)) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "video", "pion", WithRTPStreamID("h")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeOpus}, "video", "pion", WithRTPStreamID("h"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderBaseEncodingMismatch, rtpSender.AddEncoding(track1)) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("q"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderRIDCollision, rtpSender.AddEncoding(track1)) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("h"), + ) assert.NoError(t, err) assert.NoError(t, rtpSender.AddEncoding(track1)) err = rtpSender.Send(rtpSender.GetParameters()) assert.NoError(t, err) - track1, err = NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("f")) + track1, err = NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion", WithRTPStreamID("f"), + ) assert.NoError(t, err) assert.Equal(t, errRTPSenderSendAlreadyCalled, rtpSender.AddEncoding(track1)) @@ -420,17 +439,17 @@ func Test_RTPSender_FEC_Support(t *testing.T) { }) t.Run("FEC can be enabled", func(t *testing.T) { - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 94, }, RTPCodecTypeVideo)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeFlexFEC, 90000, 0, "", nil}, PayloadType: 95, }, RTPCodecTypeVideo)) - api := NewAPI(WithMediaEngine(&m)) + api := NewAPI(WithMediaEngine(&mediaEngine)) track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") assert.NoError(t, err) @@ -463,12 +482,12 @@ func Test_RTPSender_RTX_Support(t *testing.T) { }) t.Run("RTX can be disabled", func(t *testing.T) { - m := MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ + mediaEngine := MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil}, PayloadType: 94, }, RTPCodecTypeVideo)) - api := NewAPI(WithMediaEngine(&m)) + api := NewAPI(WithMediaEngine(&mediaEngine)) track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion") assert.NoError(t, err) @@ -519,5 +538,6 @@ func (s *TrackLocalCheckRTCPReaderOnBind) Bind(ctx TrackLocalContext) (RTPCodecP assert.NotNil(s.t, ctx.RTCPReader()) p, err := s.TrackLocalStaticSample.Bind(ctx) close(s.bindCalled) + return p, err } diff --git a/rtpsendparameters.go b/rtpsendparameters.go index b955c61c..68572919 100644 --- a/rtpsendparameters.go +++ b/rtpsendparameters.go @@ -3,7 +3,7 @@ package webrtc -// RTPSendParameters contains the RTP stack settings used by receivers +// RTPSendParameters contains the RTP stack settings used by receivers. type RTPSendParameters struct { RTPParameters Encodings []RTPEncodingParameters diff --git a/rtptransceiver.go b/rtptransceiver.go index 74acd664..5ce0f2d4 100644 --- a/rtptransceiver.go +++ b/rtptransceiver.go @@ -42,26 +42,30 @@ func newRTPTransceiver( t.setSender(sender) t.setDirection(direction) t.setCurrentDirection(RTPTransceiverDirectionUnknown) + return t } // SetCodecPreferences sets preferred list of supported codecs -// if codecs is empty or nil we reset to default from MediaEngine +// if codecs is empty or nil we reset to default from MediaEngine. func (t *RTPTransceiver) SetCodecPreferences(codecs []RTPCodecParameters) error { t.mu.Lock() defer t.mu.Unlock() for _, codec := range codecs { - if _, matchType := codecParametersFuzzySearch(codec, t.api.mediaEngine.getCodecsByKind(t.kind)); matchType == codecMatchNone { + if _, matchType := codecParametersFuzzySearch( + codec, t.api.mediaEngine.getCodecsByKind(t.kind), + ); matchType == codecMatchNone { return fmt.Errorf("%w %s", errRTPTransceiverCodecUnsupported, codec.MimeType) } } t.codecs = codecs + return nil } -// Codecs returns list of supported codecs +// Codecs returns list of supported codecs. func (t *RTPTransceiver) getCodecs() []RTPCodecParameters { t.mu.RLock() defer t.mu.RUnlock() @@ -85,7 +89,7 @@ func (t *RTPTransceiver) getCodecs() []RTPCodecParameters { return filteredCodecs } -// Sender returns the RTPTransceiver's RTPSender if it has one +// Sender returns the RTPTransceiver's RTPSender if it has one. func (t *RTPTransceiver) Sender() *RTPSender { if v, ok := t.sender.Load().(*RTPSender); ok { return v @@ -94,9 +98,10 @@ func (t *RTPTransceiver) Sender() *RTPSender { return nil } -// SetSender sets the RTPSender and Track to current transceiver +// SetSender sets the RTPSender and Track to current transceiver. func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) error { t.setSender(s) + return t.setSendingTrack(track) } @@ -112,7 +117,7 @@ func (t *RTPTransceiver) setSender(s *RTPSender) { t.sender.Store(s) } -// Receiver returns the RTPTransceiver's RTPReceiver if it has one +// Receiver returns the RTPTransceiver's RTPReceiver if it has one. func (t *RTPTransceiver) Receiver() *RTPReceiver { if v, ok := t.receiver.Load().(*RTPReceiver); ok { return v @@ -127,6 +132,7 @@ func (t *RTPTransceiver) SetMid(mid string) error { return fmt.Errorf("%w: %s to %s", errRTPTransceiverCannotChangeMid, currentMid, mid) } t.mid.Store(mid) + return nil } @@ -135,6 +141,7 @@ func (t *RTPTransceiver) Mid() string { if v, ok := t.mid.Load().(string); ok { return v } + return "" } @@ -143,15 +150,16 @@ func (t *RTPTransceiver) Kind() RTPCodecType { return t.kind } -// Direction returns the RTPTransceiver's current direction +// Direction returns the RTPTransceiver's current direction. func (t *RTPTransceiver) Direction() RTPTransceiverDirection { if direction, ok := t.direction.Load().(RTPTransceiverDirection); ok { return direction } + return RTPTransceiverDirection(0) } -// Stop irreversibly stops the RTPTransceiver +// Stop irreversibly stops the RTPTransceiver. func (t *RTPTransceiver) Stop() error { if sender := t.Sender(); sender != nil { if err := sender.Stop(); err != nil { @@ -166,6 +174,7 @@ func (t *RTPTransceiver) Stop() error { t.setDirection(RTPTransceiverDirectionInactive) t.setCurrentDirection(RTPTransceiverDirectionInactive) + return nil } @@ -193,10 +202,11 @@ func (t *RTPTransceiver) getCurrentDirection() RTPTransceiverDirection { if v, ok := t.currentDirection.Load().(RTPTransceiverDirection); ok { return v } + return RTPTransceiverDirectionUnknown } -func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error { +func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error { //nolint:cyclop if err := t.Sender().ReplaceTrack(track); err != nil { return err } @@ -222,6 +232,7 @@ func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error { default: return errRTPTransceiverSetSendingInvalidState } + return nil } @@ -236,13 +247,21 @@ func findByMid(mid string, localTransceivers []*RTPTransceiver) (*RTPTransceiver } // Given a direction+type pluck a transceiver from the passed list -// if no entry satisfies the requested type+direction return a inactive Transceiver -func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransceiverDirection, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) { +// if no entry satisfies the requested type+direction return a inactive Transceiver. +func satisfyTypeAndDirection( + remoteKind RTPCodecType, + remoteDirection RTPTransceiverDirection, + localTransceivers []*RTPTransceiver, +) (*RTPTransceiver, []*RTPTransceiver) { // Get direction order from most preferred to least getPreferredDirections := func() []RTPTransceiverDirection { switch remoteDirection { case RTPTransceiverDirectionSendrecv: - return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionSendonly} + return []RTPTransceiverDirection{ + RTPTransceiverDirectionRecvonly, + RTPTransceiverDirectionSendrecv, + RTPTransceiverDirectionSendonly, + } case RTPTransceiverDirectionSendonly: return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv} case RTPTransceiverDirectionRecvonly: @@ -265,11 +284,17 @@ func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransce } // handleUnknownRTPPacket consumes a single RTP Packet and returns information that is helpful -// for demuxing and handling an unknown SSRC (usually for Simulcast) -func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID, repairStreamIDExtensionID uint8, mid, rid, rsid *string) (payloadType PayloadType, paddingOnly bool, err error) { +// for demuxing and handling an unknown SSRC (usually for Simulcast). +func handleUnknownRTPPacket( + buf []byte, + midExtensionID, + streamIDExtensionID, + repairStreamIDExtensionID uint8, + mid, rid, rsid *string, +) (payloadType PayloadType, paddingOnly bool, err error) { rp := &rtp.Packet{} if err = rp.Unmarshal(buf); err != nil { - return + return 0, false, err } if rp.Padding && len(rp.Payload) == 0 { @@ -277,7 +302,7 @@ func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID, rep } if !rp.Header.Extension { - return + return payloadType, paddingOnly, nil } payloadType = PayloadType(rp.PayloadType) @@ -293,5 +318,5 @@ func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID, rep *rsid = string(payload) } - return + return payloadType, paddingOnly, nil } diff --git a/rtptransceiver_test.go b/rtptransceiver_test.go index 8c64fe47..b7e1a722 100644 --- a/rtptransceiver_test.go +++ b/rtptransceiver_test.go @@ -14,15 +14,15 @@ import ( ) func Test_RTPTransceiver_SetCodecPreferences(t *testing.T) { - me := &MediaEngine{} - api := NewAPI(WithMediaEngine(me)) - assert.NoError(t, me.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + api := NewAPI(WithMediaEngine(mediaEngine)) + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) - me.pushCodecs(me.videoCodecs, RTPCodecTypeVideo) - me.pushCodecs(me.audioCodecs, RTPCodecTypeAudio) + mediaEngine.pushCodecs(mediaEngine.videoCodecs, RTPCodecTypeVideo) + mediaEngine.pushCodecs(mediaEngine.audioCodecs, RTPCodecTypeAudio) - tr := RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs} - assert.EqualValues(t, me.videoCodecs, tr.getCodecs()) + tr := RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: mediaEngine.videoCodecs} + assert.EqualValues(t, mediaEngine.videoCodecs, tr.getCodecs()) failTestCases := [][]RTPCodecParameters{ { @@ -86,22 +86,22 @@ func Test_RTPTransceiver_SetCodecPreferences(t *testing.T) { assert.NotEqual(t, 0, len(tr.getCodecs())) } -// Assert that SetCodecPreferences properly filters codecs and PayloadTypes are respected +// Assert that SetCodecPreferences properly filters codecs and PayloadTypes are respected. func Test_RTPTransceiver_SetCodecPreferences_PayloadType(t *testing.T) { testCodec := RTPCodecParameters{ RTPCodecCapability: RTPCodecCapability{"video/testCodec", 90000, 0, "", nil}, PayloadType: 50, } - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) - offerPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + offerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) - assert.NoError(t, m.RegisterCodec(testCodec, RTPCodecTypeVideo)) + assert.NoError(t, mediaEngine.RegisterCodec(testCodec, RTPCodecTypeVideo)) - answerPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + answerPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) _, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo) diff --git a/rtptransceiverdirection.go b/rtptransceiverdirection.go index 5e1a5be1..b360ce4d 100644 --- a/rtptransceiverdirection.go +++ b/rtptransceiverdirection.go @@ -7,7 +7,7 @@ package webrtc type RTPTransceiverDirection int const ( - // RTPTransceiverDirectionUnknown is the enum's zero-value + // RTPTransceiverDirectionUnknown is the enum's zero-value. RTPTransceiverDirectionUnknown RTPTransceiverDirection = iota // RTPTransceiverDirectionSendrecv indicates the RTPSender will offer @@ -67,7 +67,7 @@ func (t RTPTransceiverDirection) String() string { } } -// Revers indicate the opposite direction +// Revers indicate the opposite direction. func (t RTPTransceiverDirection) Revers() RTPTransceiverDirection { switch t { case RTPTransceiverDirectionSendonly: @@ -79,7 +79,10 @@ func (t RTPTransceiverDirection) Revers() RTPTransceiverDirection { } } -func haveRTPTransceiverDirectionIntersection(haystack []RTPTransceiverDirection, needle []RTPTransceiverDirection) bool { +func haveRTPTransceiverDirectionIntersection( + haystack []RTPTransceiverDirection, + needle []RTPTransceiverDirection, +) bool { for _, n := range needle { for _, h := range haystack { if n == h { @@ -87,5 +90,6 @@ func haveRTPTransceiverDirectionIntersection(haystack []RTPTransceiverDirection, } } } + return false } diff --git a/rtptransceiverinit.go b/rtptransceiverinit.go index 1ad9bb60..e4dd8fe2 100644 --- a/rtptransceiverinit.go +++ b/rtptransceiverinit.go @@ -3,7 +3,8 @@ package webrtc -// RTPTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() to provide configuration options for the new transceiver. +// RTPTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() +// to provide configuration options for the new transceiver. type RTPTransceiverInit struct { Direction RTPTransceiverDirection SendEncodings []RTPEncodingParameters diff --git a/rtptransceiverinit_go_test.go b/rtptransceiverinit_go_test.go index b3268740..812cb975 100644 --- a/rtptransceiverinit_go_test.go +++ b/rtptransceiverinit_go_test.go @@ -47,7 +47,7 @@ func Test_RTPTransceiverInit_SSRC(t *testing.T) { }) assert.NoError(t, err) assert.NoError(t, signalPair(offerer, answerer)) - sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track}) + sendVideoUntilDone(t, ctx.Done(), []*TrackLocalStaticSample{track}) closePairNow(t, offerer, answerer) }) @@ -73,7 +73,7 @@ func Test_RTPTransceiverInit_SSRC(t *testing.T) { }) assert.NoError(t, err) assert.NoError(t, signalPair(offerer, answerer)) - sendVideoUntilDone(ctx.Done(), t, []*TrackLocalStaticSample{track}) + sendVideoUntilDone(t, ctx.Done(), []*TrackLocalStaticSample{track}) closePairNow(t, offerer, answerer) }) } diff --git a/sctptransport.go b/sctptransport.go index 6f03d1c0..c54bf39a 100644 --- a/sctptransport.go +++ b/sctptransport.go @@ -132,6 +132,7 @@ func (r *SCTPTransport) Start(_ SCTPCapabilities) error { err := d.open(r) if err != nil { r.log.Warnf("failed to open data channel: %s", err) + continue } openedDCCount++ @@ -147,7 +148,7 @@ func (r *SCTPTransport) Start(_ SCTPCapabilities) error { return nil } -// Stop stops the SCTPTransport +// Stop stops the SCTPTransport. func (r *SCTPTransport) Stop() error { r.lock.Lock() defer r.lock.Unlock() @@ -163,7 +164,11 @@ func (r *SCTPTransport) Stop() error { return nil } -func (r *SCTPTransport) acceptDataChannels(a *sctp.Association, existingDataChannels []*DataChannel) { +//nolint:cyclop +func (r *SCTPTransport) acceptDataChannels( + assoc *sctp.Association, + existingDataChannels []*DataChannel, +) { dataChannels := make([]*datachannel.DataChannel, 0, len(existingDataChannels)) for _, dc := range existingDataChannels { dc.mu.Lock() @@ -176,7 +181,7 @@ func (r *SCTPTransport) acceptDataChannels(a *sctp.Association, existingDataChan } ACCEPT: for { - dc, err := datachannel.Accept(a, &datachannel.Config{ + dc, err := datachannel.Accept(assoc, &datachannel.Config{ LoggerFactory: r.api.settingEngine.LoggerFactory, }, dataChannels...) if err != nil { @@ -187,6 +192,7 @@ ACCEPT: } else { r.onClose(nil) } + return } for _, ch := range dataChannels { @@ -199,7 +205,7 @@ ACCEPT: maxRetransmits *uint16 maxPacketLifeTime *uint16 ) - val := uint16(dc.Config.ReliabilityParameter) + val := uint16(dc.Config.ReliabilityParameter) //nolint:gosec //G115 ordered := true switch dc.Config.ChannelType { @@ -300,7 +306,7 @@ func (r *SCTPTransport) OnDataChannel(f func(*DataChannel)) { } // OnDataChannelOpened sets an event handler which is invoked when a data -// channel is opened +// channel is opened. func (r *SCTPTransport) OnDataChannelOpened(f func(*DataChannel)) { r.lock.Lock() defer r.lock.Unlock() @@ -324,6 +330,7 @@ func (r *SCTPTransport) onDataChannel(dc *DataChannel) (done chan struct{}) { done = make(chan struct{}) if handler == nil || dc == nil { close(done) + return } @@ -384,10 +391,11 @@ func (r *SCTPTransport) MaxChannels() uint16 { return *r.maxChannels } -// State returns the current state of the SCTPTransport +// State returns the current state of the SCTPTransport. func (r *SCTPTransport) State() SCTPTransportState { r.lock.RLock() defer r.lock.RUnlock() + return r.state } @@ -430,6 +438,7 @@ func (r *SCTPTransport) generateAndSetDataChannelID(dtlsRole DTLSRole, idOut **u } *idOut = &id r.dataChannelIDsUsed[id] = struct{}{} + return nil } @@ -443,5 +452,6 @@ func (r *SCTPTransport) association() *sctp.Association { r.lock.RLock() association := r.sctpAssociation r.lock.RUnlock() + return association } diff --git a/sctptransport_test.go b/sctptransport_test.go index ff163414..541f800c 100644 --- a/sctptransport_test.go +++ b/sctptransport_test.go @@ -52,6 +52,7 @@ func TestGenerateDataChannelID(t *testing.T) { err := testCase.s.generateAndSetDataChannelID(testCase.role, &idPtr) if err != nil { t.Errorf("failed to generate id: %v", err) + return } if *idPtr != testCase.result { @@ -87,6 +88,7 @@ func TestSCTPTransportOnClose(t *testing.T) { dc, createErr := offerPC.CreateDataChannel(expectedLabel, nil) if createErr != nil { t.Errorf("Failed to create a PC pair for testing") + return } dc.OnMessage(func(msg DataChannelMessage) { @@ -128,7 +130,8 @@ func TestSCTPTransportOnClose(t *testing.T) { } } -func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { +func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { //nolint:cyclop + // nolint:varnamelen const N = 10 done := make(chan struct{}, N) for i := 0; i < N; i++ { @@ -143,11 +146,13 @@ func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { offerPC, err := api.NewPeerConnection(config) if err != nil { t.Error(err) + return } answerPC, err := api.NewPeerConnection(config) if err != nil { t.Error(err) + return } @@ -163,6 +168,7 @@ func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { }) if err != nil { t.Error(err) + return } dc1.OnOpen(func() { @@ -177,6 +183,7 @@ func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { }) if err != nil { t.Error(err) + return } dc2.OnOpen(func() { @@ -198,18 +205,21 @@ func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { case <-connestd: case <-time.After(10 * time.Second): t.Error("conn establishment timed out") + return } <-readDetach err1 := dc1.dataChannel.SetReadDeadline(time.Now().Add(10 * time.Second)) if err1 != nil { t.Error(err) + return } buf := make([]byte, 10) n, err1 := dc1.dataChannel.Read(buf) if err1 != nil { t.Error(err) + return } if string(buf[:n]) != "hello" { @@ -228,6 +238,7 @@ func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T) { case <-connestd: case <-time.After(10 * time.Second): t.Error("connection establishment timed out") + return } <-writeDetach diff --git a/sctptransportstate.go b/sctptransportstate.go index 9deb7381..1103305f 100644 --- a/sctptransportstate.go +++ b/sctptransportstate.go @@ -7,7 +7,7 @@ package webrtc type SCTPTransportState int const ( - // SCTPTransportStateUnknown is the enum's zero-value + // SCTPTransportStateUnknown is the enum's zero-value. SCTPTransportStateUnknown SCTPTransportState = iota // SCTPTransportStateConnecting indicates the SCTPTransport is in the diff --git a/sdp.go b/sdp.go index b07a5532..d0ff51eb 100644 --- a/sdp.go +++ b/sdp.go @@ -21,7 +21,7 @@ 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 +// This isn't keyed by SSRC because it also needs to support rid based sources. type trackDetails struct { mid string kind RTPCodecType @@ -40,6 +40,7 @@ func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackDetails { } } } + return nil } @@ -55,6 +56,7 @@ func trackDetailsForRID(trackDetails []trackDetails, mid, rid string) *trackDeta } } } + return nil } @@ -80,7 +82,12 @@ func filterTrackWithSSRC(incomingTracks []trackDetails, ssrc SSRC) []trackDetail } // extract all trackDetails from an SDP. -func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) (incomingTracks []trackDetails) { // nolint:gocognit +// +//nolint:gocognit,gocyclo,cyclop +func trackDetailsFromSDP( + log logging.LeveledLogger, + s *sdp.SessionDescription, +) (incomingTracks []trackDetails) { for _, media := range s.MediaDescriptions { tracksInMediaSection := []trackDetails{} rtxRepairFlows := map[uint64]uint64{} @@ -110,7 +117,7 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( switch attr.Key { case sdp.AttrKeySSRCGroup: split := strings.Split(attr.Value, " ") - if split[0] == sdp.SemanticTokenFlowIdentification { + if split[0] == sdp.SemanticTokenFlowIdentification { //nolint:nestif // Add rtx ssrcs to blacklist, to avoid adding them as tracks // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first @@ -119,15 +126,20 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( baseSsrc, err := strconv.ParseUint(split[1], 10, 32) if err != nil { log.Warnf("Failed to parse SSRC: %v", err) + continue } rtxRepairFlow, err := strconv.ParseUint(split[2], 10, 32) if err != nil { log.Warnf("Failed to parse SSRC: %v", err) + continue } rtxRepairFlows[rtxRepairFlow] = baseSsrc - tracksInMediaSection = filterTrackWithSSRC(tracksInMediaSection, SSRC(rtxRepairFlow)) // Remove if rtx was added as track before + tracksInMediaSection = filterTrackWithSSRC( + tracksInMediaSection, + SSRC(rtxRepairFlow), + ) // Remove if rtx was added as track before for i := range tracksInMediaSection { if tracksInMediaSection[i].ssrcs[0] == SSRC(baseSsrc) { repairSsrc := SSRC(rtxRepairFlow) @@ -152,6 +164,7 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( ssrc, err := strconv.ParseUint(split[0], 10, 32) if err != nil { log.Warnf("Failed to parse SSRC: %v", err) + continue } @@ -183,7 +196,7 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( for r, baseSsrc := range rtxRepairFlows { if baseSsrc == ssrc { - repairSsrc := SSRC(r) + repairSsrc := SSRC(r) //nolint:gosec // G115 trackDetails.repairSsrc = &repairSsrc } } @@ -215,23 +228,23 @@ func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) ( return incomingTracks } -func trackDetailsToRTPReceiveParameters(t *trackDetails) RTPReceiveParameters { - encodingSize := len(t.ssrcs) - if len(t.rids) >= encodingSize { - encodingSize = len(t.rids) +func trackDetailsToRTPReceiveParameters(trackDetails *trackDetails) RTPReceiveParameters { + encodingSize := len(trackDetails.ssrcs) + if len(trackDetails.rids) >= encodingSize { + encodingSize = len(trackDetails.rids) } encodings := make([]RTPDecodingParameters, encodingSize) for i := range encodings { - if len(t.rids) > i { - encodings[i].RID = t.rids[i] + if len(trackDetails.rids) > i { + encodings[i].RID = trackDetails.rids[i] } - if len(t.ssrcs) > i { - encodings[i].SSRC = t.ssrcs[i] + if len(trackDetails.ssrcs) > i { + encodings[i].SSRC = trackDetails.ssrcs[i] } - if t.repairSsrc != nil { - encodings[i].RTX.SSRC = *t.repairSsrc + if trackDetails.repairSsrc != nil { + encodings[i].RTX.SSRC = *trackDetails.repairSsrc } } @@ -261,16 +274,22 @@ func getRids(media *sdp.MediaDescription) []*simulcastRid { for _, rid := range rids { if rid.id == ridID { rid.paused = true + break } } } } } + return rids } -func addCandidatesToMediaDescriptions(candidates []ICECandidate, m *sdp.MediaDescription, iceGatheringState ICEGatheringState) error { +func addCandidatesToMediaDescriptions( + candidates []ICECandidate, + mediaDescr *sdp.MediaDescription, + iceGatheringState ICEGatheringState, +) error { appendCandidateIfNew := func(c ice.Candidate, attributes []sdp.Attribute) { marshaled := c.Marshal() for _, a := range attributes { @@ -279,7 +298,7 @@ func addCandidatesToMediaDescriptions(candidates []ICECandidate, m *sdp.MediaDes } } - m.WithValueAttribute("candidate", marshaled) + mediaDescr.WithValueAttribute("candidate", marshaled) } for _, c := range candidates { @@ -289,26 +308,36 @@ func addCandidatesToMediaDescriptions(candidates []ICECandidate, m *sdp.MediaDes } candidate.SetComponent(1) - appendCandidateIfNew(candidate, m.Attributes) + appendCandidateIfNew(candidate, mediaDescr.Attributes) candidate.SetComponent(2) - appendCandidateIfNew(candidate, m.Attributes) + appendCandidateIfNew(candidate, mediaDescr.Attributes) } if iceGatheringState != ICEGatheringStateComplete { return nil } - for _, a := range m.Attributes { + for _, a := range mediaDescr.Attributes { if a.Key == "end-of-candidates" { return nil } } - m.WithPropertyAttribute("end-of-candidates") + mediaDescr.WithPropertyAttribute("end-of-candidates") + return nil } -func addDataMediaSection(d *sdp.SessionDescription, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState) error { +func addDataMediaSection( + descr *sdp.SessionDescription, + shouldAddCandidates bool, + dtlsFingerprints []DTLSFingerprint, + midValue string, + iceParams ICEParameters, + candidates []ICECandidate, + dtlsRole sdp.ConnectionRole, + iceGatheringState ICEGatheringState, +) error { media := (&sdp.MediaDescription{ MediaName: sdp.MediaName{ Media: mediaSectionApplication, @@ -340,11 +369,16 @@ func addDataMediaSection(d *sdp.SessionDescription, shouldAddCandidates bool, dt } } - d.WithMedia(media) + descr.WithMedia(media) + return nil } -func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGatherer, iceGatheringState ICEGatheringState) *SessionDescription { +func populateLocalCandidates( + sessionDescription *SessionDescription, + i *ICEGatherer, + iceGatheringState ICEGatheringState, +) *SessionDescription { if sessionDescription == nil || i == nil { return sessionDescription } @@ -356,8 +390,8 @@ func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGathe parsed := sessionDescription.parsed if len(parsed.MediaDescriptions) > 0 { - m := parsed.MediaDescriptions[0] - if err = addCandidatesToMediaDescriptions(candidates, m, iceGatheringState); err != nil { + mediaDescr := parsed.MediaDescriptions[0] + if err = addCandidatesToMediaDescriptions(candidates, mediaDescr, iceGatheringState); err != nil { return sessionDescription } } @@ -374,7 +408,7 @@ func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGathe } } -// nolint: gocognit +//nolint:gocognit,cyclop func addSenderSDP( mediaSection mediaSection, isPlanB bool, @@ -400,14 +434,29 @@ func addSenderSDP( media = media.WithValueAttribute("ssrc-group", fmt.Sprintf("FEC-FR %d %d", encoding.SSRC, encoding.FEC.SSRC)) } - media = media.WithMediaSource(uint32(encoding.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID()) + media = media.WithMediaSource( + uint32(encoding.SSRC), + track.StreamID(), /* cname */ + track.StreamID(), /* streamLabel */ + track.ID(), + ) if !isPlanB { if encoding.RTX.SSRC != 0 { - media = media.WithMediaSource(uint32(encoding.RTX.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID()) + media = media.WithMediaSource( + uint32(encoding.RTX.SSRC), + track.StreamID(), /* cname */ + track.StreamID(), /* streamLabel */ + track.ID(), + ) } if encoding.FEC.SSRC != 0 { - media = media.WithMediaSource(uint32(encoding.FEC.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID()) + media = media.WithMediaSource( + uint32(encoding.FEC.SSRC), + track.StreamID(), /* cname */ + track.StreamID(), /* streamLabel */ + track.ID(), + ) } media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID()) @@ -431,8 +480,9 @@ func addSenderSDP( } } +//nolint:cyclop func addTransceiverSDP( - d *sdp.SessionDescription, + descr *sdp.SessionDescription, isPlanB bool, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, @@ -449,15 +499,15 @@ func addTransceiverSDP( return false, errSDPZeroTransceivers } // Use the first transceiver to generate the section attributes - t := transceivers[0] - media := sdp.NewJSEPMediaDescription(t.kind.String(), []string{}). + transceiver := transceivers[0] + media := sdp.NewJSEPMediaDescription(transceiver.kind.String(), []string{}). WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()). WithValueAttribute(sdp.AttrKeyMID, midValue). WithICECredentials(iceParams.UsernameFragment, iceParams.Password). WithPropertyAttribute(sdp.AttrKeyRTCPMux). WithPropertyAttribute(sdp.AttrKeyRTCPRsize) - codecs := t.getCodecs() + codecs := transceiver.getCodecs() for _, codec := range codecs { name := strings.TrimPrefix(codec.MimeType, "audio/") name = strings.TrimPrefix(name, "video/") @@ -469,18 +519,20 @@ func addTransceiverSDP( } if len(codecs) == 0 { // If we are sender and we have no codecs throw an error early - if t.Sender() != nil { + if transceiver.Sender() != nil { return false, ErrSenderWithNoCodecs } // Explicitly reject track if we don't have the codec // We need to include connection information even if we're rejecting a track, otherwise Firefox will fail to // parse the SDP with an error like: - // SIPCC Failed to parse SDP: SDP Parse Error on line 50: c= connection line not specified for every media level, validation failed. - // In addition this makes our SDP compliant with RFC 4566 Section 5.7: https://datatracker.ietf.org/doc/html/rfc4566#section-5.7 - d.WithMedia(&sdp.MediaDescription{ + // SIPCC Failed to parse SDP: SDP Parse Error on line 50: c= connection line not specified for every media level, + // validation failed. + // In addition this makes our SDP compliant with RFC 4566 Section 5.7: + // https://datatracker.ietf.org/doc/html/rfc4566#section-5.7 + descr.WithMedia(&sdp.MediaDescription{ MediaName: sdp.MediaName{ - Media: t.kind.String(), + Media: transceiver.kind.String(), Port: sdp.RangedPort{Value: 0}, Protos: []string{"UDP", "TLS", "RTP", "SAVPF"}, Formats: []string{"0"}, @@ -493,18 +545,19 @@ func addTransceiverSDP( }, }, }) + return false, nil } directions := []RTPTransceiverDirection{} - if t.Sender() != nil { + if transceiver.Sender() != nil { directions = append(directions, RTPTransceiverDirectionSendonly) } - if t.Receiver() != nil { + if transceiver.Receiver() != nil { directions = append(directions, RTPTransceiverDirectionRecvonly) } - parameters := mediaEngine.getRTPParametersByKind(t.kind, directions) + parameters := mediaEngine.getRTPParametersByKind(transceiver.kind, directions) for _, rtpExtension := range parameters.HeaderExtensions { if mediaSection.matchExtensions != nil { if _, enabled := mediaSection.matchExtensions[rtpExtension.URI]; !enabled { @@ -535,7 +588,7 @@ func addTransceiverSDP( addSenderSDP(mediaSection, isPlanB, media) - media = media.WithPropertyAttribute(t.Direction().String()) + media = media.WithPropertyAttribute(transceiver.Direction().String()) for _, fingerprint := range dtlsFingerprints { media = media.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value)) @@ -547,7 +600,7 @@ func addTransceiverSDP( } } - d.WithMedia(media) + descr.WithMedia(media) return true, nil } @@ -573,19 +626,23 @@ func bundleMatchFromRemote(matchBundleGroup *string) func(mid string) bool { } } bundleTags := strings.Split(*matchBundleGroup, " ") + return func(midValue string) bool { for _, tag := range bundleTags { if tag == midValue { return true } } + return false } } -// populateSDP serializes a PeerConnections state into an SDP +// populateSDP serializes a PeerConnections state into an SDP. +// +//nolint:cyclop func populateSDP( - d *sdp.SessionDescription, + descr *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTLSFingerprint, mediaDescriptionFingerprint bool, @@ -615,54 +672,76 @@ func populateSDP( bundleCount++ } - for i, m := range mediaSections { - if m.data && len(m.transceivers) != 0 { + for i, section := range mediaSections { + if section.data && len(section.transceivers) != 0 { return nil, errSDPMediaSectionMediaDataChanInvalid - } else if !isPlanB && len(m.transceivers) > 1 { + } else if !isPlanB && len(section.transceivers) > 1 { return nil, errSDPMediaSectionMultipleTrackInvalid } shouldAddID := true shouldAddCandidates := i == 0 - if m.data { - if err = addDataMediaSection(d, shouldAddCandidates, mediaDtlsFingerprints, m.id, iceParams, candidates, connectionRole, iceGatheringState); err != nil { + if section.data { + if err = addDataMediaSection( + descr, + shouldAddCandidates, + mediaDtlsFingerprints, + section.id, + iceParams, + candidates, + connectionRole, + iceGatheringState, + ); err != nil { return nil, err } } else { - shouldAddID, err = addTransceiverSDP(d, isPlanB, shouldAddCandidates, mediaDtlsFingerprints, mediaEngine, m.id, iceParams, candidates, connectionRole, iceGatheringState, m) + shouldAddID, err = addTransceiverSDP( + descr, + isPlanB, + shouldAddCandidates, + mediaDtlsFingerprints, + mediaEngine, + section.id, + iceParams, + candidates, + connectionRole, + iceGatheringState, + section, + ) if err != nil { return nil, err } } if shouldAddID { - if bundleMatch(m.id) { - appendBundle(m.id) + if bundleMatch(section.id) { + appendBundle(section.id) } else { - d.MediaDescriptions[len(d.MediaDescriptions)-1].MediaName.Port = sdp.RangedPort{Value: 0} + descr.MediaDescriptions[len(descr.MediaDescriptions)-1].MediaName.Port = sdp.RangedPort{Value: 0} } } } if !mediaDescriptionFingerprint { for _, fingerprint := range dtlsFingerprints { - d.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value)) + descr.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value)) } } if isICELite { // RFC 5245 S15.3 - d = d.WithValueAttribute(sdp.AttrKeyICELite, "") + descr = descr.WithValueAttribute(sdp.AttrKeyICELite, "") } if isExtmapAllowMixed { - d = d.WithPropertyAttribute(sdp.AttrKeyExtMapAllowMixed) + descr = descr.WithPropertyAttribute(sdp.AttrKeyExtMapAllowMixed) } if bundleCount > 0 { - d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue) + descr = descr.WithValueAttribute(sdp.AttrKeyGroup, bundleValue) } - return d, nil + + return descr, nil } func getMidValue(media *sdp.MediaDescription) string { @@ -671,10 +750,11 @@ func getMidValue(media *sdp.MediaDescription) string { return attr.Value } } + return "" } -// SessionDescription contains a MediaSection with Multiple SSRCs, it is Plan-B +// SessionDescription contains a MediaSection with Multiple SSRCs, it is Plan-B. func descriptionIsPlanB(desc *SessionDescription, log logging.LeveledLogger) bool { if desc == nil || desc.parsed == nil { return false @@ -695,7 +775,7 @@ func descriptionIsPlanB(desc *SessionDescription, log logging.LeveledLogger) boo // SessionDescription contains a MediaSection with name `audio`, `video` or `data` // If only one SSRC is set we can't know if it is Plan-B or Unified. If users have -// set fallback mode assume it is Plan-B +// set fallback mode assume it is Plan-B. func descriptionPossiblyPlanB(desc *SessionDescription) bool { if desc == nil || desc.parsed == nil { return false @@ -707,6 +787,7 @@ func descriptionPossiblyPlanB(desc *SessionDescription) bool { return true } } + return false } @@ -716,6 +797,7 @@ func getPeerDirection(media *sdp.MediaDescription) RTPTransceiverDirection { return direction } } + return RTPTransceiverDirectionUnknown } @@ -737,7 +819,7 @@ func extractBundleID(desc *sdp.SessionDescription) string { return bundleIDs[1] } -func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { //nolint: gocognit +func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { //nolint:gocognit,cyclop fingerprint := "" // Fingerprint on session level has highest priority @@ -745,14 +827,14 @@ func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { fingerprint = sessionFingerprint } - if fingerprint == "" { + if fingerprint == "" { //nolint:nestif bundleID := extractBundleID(desc) if bundleID != "" { // Locate the fingerprint of the bundled media section - for _, m := range desc.MediaDescriptions { - if mid, haveMid := m.Attribute("mid"); haveMid { + for _, mediaDescr := range desc.MediaDescriptions { + if mid, haveMid := mediaDescr.Attribute("mid"); haveMid { if mid == bundleID && fingerprint == "" { - if mediaFingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint { + if mediaFingerprint, haveFingerprint := mediaDescr.Attribute("fingerprint"); haveFingerprint { fingerprint = mediaFingerprint } } @@ -762,8 +844,8 @@ func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { // Take the fingerprint from the first media section which has one. // Note: According to Bundle spec each media section would have it's own transport // with it's own cert and fingerprint each, so we would need to return a list. - for _, m := range desc.MediaDescriptions { - mediaFingerprint, haveFingerprint := m.Attribute("fingerprint") + for _, mediaDescr := range desc.MediaDescriptions { + mediaFingerprint, haveFingerprint := mediaDescr.Attribute("fingerprint") if haveFingerprint && fingerprint == "" { fingerprint = mediaFingerprint } @@ -779,17 +861,21 @@ func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) { if len(parts) != 2 { return "", "", ErrSessionDescriptionInvalidFingerprint } + return parts[1], parts[0], nil } -// identifiedMediaDescription contains a MediaDescription with sdpMid and sdpMLineIndex +// identifiedMediaDescription contains a MediaDescription with sdpMid and sdpMLineIndex. type identifiedMediaDescription struct { MediaDescription *sdp.MediaDescription SDPMid string SDPMLineIndex uint16 } -func extractICEDetailsFromMedia(media *identifiedMediaDescription, log logging.LeveledLogger) (string, string, []ICECandidate, error) { +func extractICEDetailsFromMedia( + media *identifiedMediaDescription, + log logging.LeveledLogger, +) (string, string, []ICECandidate, error) { remoteUfrag := "" remotePwd := "" candidates := []ICECandidate{} @@ -807,8 +893,10 @@ func extractICEDetailsFromMedia(media *identifiedMediaDescription, log logging.L if err != nil { if errors.Is(err, ice.ErrUnknownCandidateTyp) || errors.Is(err, ice.ErrDetermineNetworkType) { log.Warnf("Discarding remote candidate: %s", err) + continue } + return "", "", nil, err } @@ -830,7 +918,10 @@ type sdpICEDetails struct { Candidates []ICECandidate } -func extractICEDetails(desc *sdp.SessionDescription, log logging.LeveledLogger) (*sdpICEDetails, error) { // nolint:gocognit +func extractICEDetails( + desc *sdp.SessionDescription, + log logging.LeveledLogger, +) (*sdpICEDetails, error) { // nolint:gocognit details := &sdpICEDetails{ Candidates: []ICECandidate{}, } @@ -870,7 +961,10 @@ func extractICEDetails(desc *sdp.SessionDescription, log logging.LeveledLogger) // Select the first media section or the first bundle section // Currently Pion uses the first media section to gather candidates. // https://github.com/pion/webrtc/pull/2950 -func selectCandidateMediaSection(sessionDescription *sdp.SessionDescription) (descr *identifiedMediaDescription, ok bool) { +func selectCandidateMediaSection(sessionDescription *sdp.SessionDescription) ( + descr *identifiedMediaDescription, + ok bool, +) { bundleID := extractBundleID(sessionDescription) for mLineIndex, mediaDescr := range sessionDescription.MediaDescriptions { @@ -881,7 +975,7 @@ func selectCandidateMediaSection(sessionDescription *sdp.SessionDescription) (de return &identifiedMediaDescription{ MediaDescription: mediaDescr, SDPMid: mid, - SDPMLineIndex: uint16(mLineIndex), + SDPMLineIndex: uint16(mLineIndex), //nolint:gosec // G115 }, true } } else { @@ -889,7 +983,7 @@ func selectCandidateMediaSection(sessionDescription *sdp.SessionDescription) (de return &identifiedMediaDescription{ MediaDescription: mediaDescr, SDPMid: mid, - SDPMLineIndex: uint16(mLineIndex), + SDPMLineIndex: uint16(mLineIndex), //nolint:gosec // G115 }, true } } @@ -898,8 +992,8 @@ func selectCandidateMediaSection(sessionDescription *sdp.SessionDescription) (de } func haveApplicationMediaSection(desc *sdp.SessionDescription) bool { - for _, m := range desc.MediaDescriptions { - if m.MediaName.Media == mediaSectionApplication { + for _, mediaDescr := range desc.MediaDescriptions { + if mediaDescr.MediaName.Media == mediaSectionApplication { return true } } @@ -913,25 +1007,27 @@ func getByMid(searchMid string, desc *SessionDescription) *sdp.MediaDescription return m } } + return nil } -// haveDataChannel return MediaDescription with MediaName equal application +// haveDataChannel return MediaDescription with MediaName equal application. func haveDataChannel(desc *SessionDescription) *sdp.MediaDescription { for _, d := range desc.parsed.MediaDescriptions { if d.MediaName.Media == mediaSectionApplication { return d } } + return nil } -func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParameters, err error) { +func codecsFromMediaDescription(mediaDescr *sdp.MediaDescription) (out []RTPCodecParameters, err error) { s := &sdp.SessionDescription{ - MediaDescriptions: []*sdp.MediaDescription{m}, + MediaDescriptions: []*sdp.MediaDescription{mediaDescr}, } - for _, payloadStr := range m.MediaName.Formats { + for _, payloadStr := range mediaDescr.MediaName.Formats { payloadType, err := strconv.ParseUint(payloadStr, 10, 8) if err != nil { return nil, err @@ -942,6 +1038,7 @@ func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParamete if payloadType == 0 { continue } + return nil, err } @@ -963,8 +1060,14 @@ func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParamete } out = append(out, RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{m.MediaName.Media + "/" + codec.Name, codec.ClockRate, channels, codec.Fmtp, feedback}, - PayloadType: PayloadType(payloadType), + RTPCodecCapability: RTPCodecCapability{ + mediaDescr.MediaName.Media + "/" + codec.Name, + codec.ClockRate, + channels, + codec.Fmtp, + feedback, + }, + PayloadType: PayloadType(payloadType), }) } @@ -992,17 +1095,17 @@ func rtpExtensionsFromMediaDescription(m *sdp.MediaDescription) (map[string]int, // for subsequent calling, it updates Origin for SessionDescription from saved one // and increments session version by one. // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-25#section-5.2.2 -func updateSDPOrigin(origin *sdp.Origin, d *sdp.SessionDescription) { - if atomic.CompareAndSwapUint64(&origin.SessionVersion, 0, d.Origin.SessionVersion) { // store - atomic.StoreUint64(&origin.SessionID, d.Origin.SessionID) +func updateSDPOrigin(origin *sdp.Origin, descr *sdp.SessionDescription) { + if atomic.CompareAndSwapUint64(&origin.SessionVersion, 0, descr.Origin.SessionVersion) { // store + atomic.StoreUint64(&origin.SessionID, descr.Origin.SessionID) } else { // load for { // awaiting for saving session id - d.Origin.SessionID = atomic.LoadUint64(&origin.SessionID) - if d.Origin.SessionID != 0 { + descr.Origin.SessionID = atomic.LoadUint64(&origin.SessionID) + if descr.Origin.SessionID != 0 { break } } - d.Origin.SessionVersion = atomic.AddUint64(&origin.SessionVersion, 1) + descr.Origin.SessionVersion = atomic.AddUint64(&origin.SessionVersion, 1) } } diff --git a/sdp_test.go b/sdp_test.go index e704a057..700f16fc 100644 --- a/sdp_test.go +++ b/sdp_test.go @@ -74,7 +74,7 @@ func TestExtractFingerprint(t *testing.T) { }) t.Run("Fingerprint from master bundle section", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ Attributes: []sdp.Attribute{ {Key: "group", Value: "BUNDLE 1 0"}, }, @@ -90,14 +90,14 @@ func TestExtractFingerprint(t *testing.T) { }, } - fingerprint, hash, err := extractFingerprint(s) + fingerprint, hash, err := extractFingerprint(descr) assert.NoError(t, err) assert.Equal(t, fingerprint, "foo") assert.Equal(t, hash, "bar") }) t.Run("Fingerprint from first media section", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ MediaDescriptions: []*sdp.MediaDescription{ {Attributes: []sdp.Attribute{ {Key: "mid", Value: "0"}, @@ -110,7 +110,7 @@ func TestExtractFingerprint(t *testing.T) { }, } - fingerprint, hash, err := extractFingerprint(s) + fingerprint, hash, err := extractFingerprint(descr) assert.NoError(t, err) assert.Equal(t, fingerprint, "boo") assert.Equal(t, hash, "zoo") @@ -267,7 +267,7 @@ func TestExtractICEDetails(t *testing.T) { }) t.Run("Extracts candidate from media section", func(t *testing.T) { - s := &sdp.SessionDescription{ + sdp := &sdp.SessionDescription{ Attributes: []sdp.Attribute{ {Key: "group", Value: "BUNDLE video audio"}, }, @@ -298,7 +298,7 @@ func TestExtractICEDetails(t *testing.T) { }, } - details, err := extractICEDetails(s, nil) + details, err := extractICEDetails(sdp, nil) assert.NoError(t, err) assert.Equal(t, details.Ufrag, "ufrag") assert.Equal(t, details.Password, "pwd") @@ -365,7 +365,7 @@ func TestSelectCandidateMediaSection(t *testing.T) { func TestTrackDetailsFromSDP(t *testing.T) { t.Run("Tracks unknown, audio and video with RTX", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ MediaDescriptions: []*sdp.MediaDescription{ { MediaName: sdp.MediaName{ @@ -422,7 +422,7 @@ func TestTrackDetailsFromSDP(t *testing.T) { }, } - tracks := trackDetailsFromSDP(nil, s) + tracks := trackDetailsFromSDP(nil, descr) assert.Equal(t, 3, len(tracks)) if trackDetail := trackDetailsForSSRC(tracks, 1000); trackDetail != nil { assert.Fail(t, "got the unknown track ssrc:1000 which should have been skipped") @@ -455,7 +455,7 @@ func TestTrackDetailsFromSDP(t *testing.T) { }) t.Run("inactive and recvonly tracks ignored", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ MediaDescriptions: []*sdp.MediaDescription{ { MediaName: sdp.MediaName{ @@ -477,11 +477,11 @@ func TestTrackDetailsFromSDP(t *testing.T) { }, }, } - assert.Equal(t, 0, len(trackDetailsFromSDP(nil, s))) + assert.Equal(t, 0, len(trackDetailsFromSDP(nil, descr))) }) t.Run("ssrc-group after ssrc", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ MediaDescriptions: []*sdp.MediaDescription{ { MediaName: sdp.MediaName{ @@ -510,7 +510,7 @@ func TestTrackDetailsFromSDP(t *testing.T) { }, } - tracks := trackDetailsFromSDP(nil, s) + tracks := trackDetailsFromSDP(nil, descr) assert.Equal(t, 2, len(tracks)) assert.Equal(t, SSRC(4000), *tracks[0].repairSsrc) assert.Equal(t, SSRC(6000), *tracks[1].repairSsrc) @@ -519,7 +519,7 @@ func TestTrackDetailsFromSDP(t *testing.T) { func TestHaveApplicationMediaSection(t *testing.T) { t.Run("Audio only", func(t *testing.T) { - s := &sdp.SessionDescription{ + descr := &sdp.SessionDescription{ MediaDescriptions: []*sdp.MediaDescription{ { MediaName: sdp.MediaName{ @@ -533,7 +533,7 @@ func TestHaveApplicationMediaSection(t *testing.T) { }, } - assert.False(t, haveApplicationMediaSection(s)) + assert.False(t, haveApplicationMediaSection(descr)) }) t.Run("Application", func(t *testing.T) { @@ -593,6 +593,8 @@ func TestMediaDescriptionFingerprints(t *testing.T) { fingerprintTest := func(SDPMediaDescriptionFingerprints bool, expectedFingerprintCount int) func(t *testing.T) { return func(t *testing.T) { + t.Helper() + s := &sdp.SessionDescription{} dtlsFingerprints, err := certificate.GetFingerprints() @@ -615,7 +617,7 @@ func TestMediaDescriptionFingerprints(t *testing.T) { t.Run("Per-Session Description Fingerprints", fingerprintTest(false, 1)) } -func TestPopulateSDP(t *testing.T) { +func TestPopulateSDP(t *testing.T) { //nolint:cyclop,maintidx t.Run("rid", func(t *testing.T) { se := SettingEngine{} @@ -640,7 +642,21 @@ func TestPopulateSDP(t *testing.T) { d := &sdp.SessionDescription{} - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) // Test contains rid map keys @@ -684,7 +700,21 @@ func TestPopulateSDP(t *testing.T) { d := &sdp.SessionDescription{} - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) // Test codecs @@ -709,7 +739,21 @@ func TestPopulateSDP(t *testing.T) { se := SettingEngine{} se.SetLite(true) - offerSdp, err := populateSDP(&sdp.SessionDescription{}, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, &MediaEngine{}, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, []mediaSection{}, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + &sdp.SessionDescription{}, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + &MediaEngine{}, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + []mediaSection{}, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) var found bool @@ -719,6 +763,7 @@ func TestPopulateSDP(t *testing.T) { // ice-lite does not have value (e.g. ":") and it should be an empty string if a.Value == "" { found = true + break } } @@ -730,19 +775,42 @@ func TestPopulateSDP(t *testing.T) { me := &MediaEngine{} registerCodecErr := me.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeVP8, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo) assert.NoError(t, registerCodecErr) api := NewAPI(WithMediaEngine(me)) videoTransceiver := &RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs} audioTransceiver := &RTPTransceiver{kind: RTPCodecTypeAudio, api: api, codecs: []RTPCodecParameters{}} - mediaSections := []mediaSection{{id: "video", transceivers: []*RTPTransceiver{videoTransceiver}}, {id: "audio", transceivers: []*RTPTransceiver{audioTransceiver}}} + mediaSections := []mediaSection{ + {id: "video", transceivers: []*RTPTransceiver{videoTransceiver}}, + {id: "audio", transceivers: []*RTPTransceiver{audioTransceiver}}, + } d := &sdp.SessionDescription{} - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + nil, + ) assert.NoError(t, err) // Test codecs @@ -760,7 +828,21 @@ func TestPopulateSDP(t *testing.T) { }) t.Run("allow mixed extmap", func(t *testing.T) { se := SettingEngine{} - offerSdp, err := populateSDP(&sdp.SessionDescription{}, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, &MediaEngine{}, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, []mediaSection{}, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + &sdp.SessionDescription{}, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + &MediaEngine{}, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + []mediaSection{}, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) var found bool @@ -769,13 +851,27 @@ func TestPopulateSDP(t *testing.T) { if a.Key == sdp.AttrKeyExtMapAllowMixed { if a.Value == "" { found = true + break } } } assert.Equal(t, true, found, "AllowMixedExtMap key should be present") - offerSdp, err = populateSDP(&sdp.SessionDescription{}, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, false, &MediaEngine{}, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, []mediaSection{}, ICEGatheringStateComplete, nil) + offerSdp, err = populateSDP( + &sdp.SessionDescription{}, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + false, &MediaEngine{}, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + []mediaSection{}, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) found = false @@ -784,6 +880,7 @@ func TestPopulateSDP(t *testing.T) { if a.Key == sdp.AttrKeyExtMapAllowMixed { if a.Value == "" { found = true + break } } @@ -803,7 +900,21 @@ func TestPopulateSDP(t *testing.T) { d := &sdp.SessionDescription{} - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, nil) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + nil, + ) assert.Nil(t, err) bundle, ok := offerSdp.Attribute(sdp.AttrKeyGroup) @@ -828,7 +939,21 @@ func TestPopulateSDP(t *testing.T) { d := &sdp.SessionDescription{} matchedBundle := "audio" - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, &matchedBundle) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + &matchedBundle, + ) assert.Nil(t, err) bundle, ok := offerSdp.Attribute(sdp.AttrKeyGroup) @@ -855,7 +980,21 @@ func TestPopulateSDP(t *testing.T) { d := &sdp.SessionDescription{} matchedBundle := "" - offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, true, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete, &matchedBundle) + offerSdp, err := populateSDP( + d, + false, + []DTLSFingerprint{}, + se.sdpMediaLevelFingerprints, + se.candidates.ICELite, + true, + me, + connectionRoleFromDtlsRole(defaultDtlsRoleOffer), + []ICECandidate{}, + ICEParameters{}, + mediaSections, + ICEGatheringStateComplete, + &matchedBundle, + ) assert.Nil(t, err) _, ok := offerSdp.Attribute(sdp.AttrKeyGroup) @@ -864,7 +1003,7 @@ func TestPopulateSDP(t *testing.T) { } func TestGetRIDs(t *testing.T) { - m := []*sdp.MediaDescription{ + mediaDescr := []*sdp.MediaDescription{ { MediaName: sdp.MediaName{ Media: "video", @@ -876,13 +1015,14 @@ func TestGetRIDs(t *testing.T) { }, } - rids := getRids(m[0]) + rids := getRids(mediaDescr[0]) assert.NotEmpty(t, rids, "Rid mapping should be present") found := false for _, rid := range rids { if rid.id == "f" { found = true + break } } @@ -930,8 +1070,18 @@ func TestCodecsFromMediaDescription(t *testing.T) { assert.Equal(t, codecs, []RTPCodecParameters{ { - RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}}}, - PayloadType: 111, + RTPCodecCapability: RTPCodecCapability{ + MimeTypeOpus, + 48000, + 2, + "minptime=10;useinbandfec=1", + []RTCPFeedback{ + {"goog-remb", ""}, + {"ccm", "fir"}, + {"nack", ""}, + }, + }, + PayloadType: 111, }, }) assert.NoError(t, err) @@ -955,7 +1105,7 @@ func TestRtpExtensionsFromMediaDescription(t *testing.T) { assert.Equal(t, extensions[sdp.SDESMidURI], 3) } -// Assert that FEC and RTX SSRCes are present if they are enabled in the MediaEngine +// Assert that FEC and RTX SSRCes are present if they are enabled in the MediaEngine. func Test_SSRC_Groups(t *testing.T) { const offerWithRTX = `v=0 o=- 930222930247584370 1727933945 IN IP4 0.0.0.0 @@ -1071,23 +1221,41 @@ a=sendrecv } } - m := &MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeOpus, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 101, + me := &MediaEngine{} + assert.NoError(t, me.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeOpus, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 101, }, RTPCodecTypeAudio)) - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + assert.NoError(t, me.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeVP8, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo)) if testCase.enableRTXInMediaEngine { - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: MimeTypeRTX, ClockRate: 90000, Channels: 0, SDPFmtpLine: "apt=96", RTCPFeedback: nil}, - PayloadType: 97, + assert.NoError(t, me.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeType: MimeTypeRTX, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "apt=96", + RTCPFeedback: nil, + }, + PayloadType: 97, }, RTPCodecTypeVideo)) } - peerConnection, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + peerConnection, err := NewAPI(WithMediaEngine(me)).NewPeerConnection(Configuration{}) assert.NoError(t, err) audioTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeOpus}, "audio-id", "stream-id") @@ -1107,7 +1275,9 @@ a=sendrecv assert.NoError(t, err) checkRTXSupport(offer.parsed) } else { - assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{Type: SDPTypeOffer, SDP: testCase.remoteOffer})) + assert.NoError(t, peerConnection.SetRemoteDescription(SessionDescription{ + Type: SDPTypeOffer, SDP: testCase.remoteOffer, + })) answer, err := peerConnection.CreateAnswer(nil) assert.NoError(t, err) checkRTXSupport(answer.parsed) diff --git a/sdpsemantics.go b/sdpsemantics.go index 689b4e95..c834167a 100644 --- a/sdpsemantics.go +++ b/sdpsemantics.go @@ -8,7 +8,7 @@ import ( ) // SDPSemantics determines which style of SDP offers and answers -// can be used +// can be used. type SDPSemantics int const ( @@ -24,7 +24,7 @@ const ( // SDPSemanticsUnifiedPlanWithFallback prefers unified-plan // offers and answers, but will respond to a plan-b offer - // with a plan-b answer + // with a plan-b answer. SDPSemanticsUnifiedPlanWithFallback ) @@ -58,7 +58,7 @@ func (s SDPSemantics) String() string { } } -// UnmarshalJSON parses the JSON-encoded data and stores the result +// UnmarshalJSON parses the JSON-encoded data and stores the result. func (s *SDPSemantics) UnmarshalJSON(b []byte) error { var val string if err := json.Unmarshal(b, &val); err != nil { @@ -66,10 +66,11 @@ func (s *SDPSemantics) UnmarshalJSON(b []byte) error { } *s = newSDPSemantics(val) + return nil } -// MarshalJSON returns the JSON encoding +// MarshalJSON returns the JSON encoding. func (s SDPSemantics) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } diff --git a/sdpsemantics_test.go b/sdpsemantics_test.go index eff518f7..31197ebf 100644 --- a/sdpsemantics_test.go +++ b/sdpsemantics_test.go @@ -81,6 +81,7 @@ func getMdNames(sdp *sdp.SessionDescription) []string { for _, media := range sdp.MediaDescriptions { mdNames = append(mdNames, media.MediaName.Media) } + return mdNames } @@ -96,6 +97,7 @@ func extractSsrcList(md *sdp.MediaDescription) []string { for ssrc := range ssrcMap { ssrcList = append(ssrcList, ssrc) } + return ssrcList } @@ -194,13 +196,19 @@ func TestSDPSemantics_PlanBAnswerSenders(t *testing.T) { }) assert.NoError(t, err) - video1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"}, "1", "1") + video1, err := NewTrackLocalStaticSample(RTPCodecCapability{ + MimeType: MimeTypeH264, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, "1", "1") assert.NoError(t, err) _, err = apc.AddTrack(video1) assert.NoError(t, err) - video2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"}, "2", "2") + video2, err := NewTrackLocalStaticSample(RTPCodecCapability{ + MimeType: MimeTypeH264, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, "2", "2") assert.NoError(t, err) _, err = apc.AddTrack(video2) @@ -269,13 +277,19 @@ func TestSDPSemantics_UnifiedPlanWithFallback(t *testing.T) { }) assert.NoError(t, err) - video1, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"}, "1", "1") + video1, err := NewTrackLocalStaticSample(RTPCodecCapability{ + MimeType: MimeTypeH264, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, "1", "1") assert.NoError(t, err) _, err = apc.AddTrack(video1) assert.NoError(t, err) - video2, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeH264, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f"}, "2", "2") + video2, err := NewTrackLocalStaticSample(RTPCodecCapability{ + MimeType: MimeTypeH264, + SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", + }, "2", "2") assert.NoError(t, err) _, err = apc.AddTrack(video2) @@ -312,9 +326,10 @@ func TestSDPSemantics_UnifiedPlanWithFallback(t *testing.T) { for ssrc := range ssrcMap { ssrcList = append(ssrcList, ssrc) } + return ssrcList } - // Verify that each section has 2 SSRCs (one for each sender) + // Verify that each section has 2 SSRCs (one for each sender). for _, section := range []string{"video", "audio"} { for _, media := range answer.parsed.MediaDescriptions { if media.MediaName.Media == section { @@ -326,9 +341,11 @@ func TestSDPSemantics_UnifiedPlanWithFallback(t *testing.T) { closePairNow(t, apc, opc) } -// Assert that we can catch Remote SessionDescription that don't match our Semantics +// Assert that we can catch Remote SessionDescription that don't match our Semantics. func TestSDPSemantics_SetRemoteDescription_Mismatch(t *testing.T) { + //nolint:lll planBOffer := "v=0\r\no=- 4648475892259889561 3 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE video audio\r\na=ice-ufrag:1hhfzwf0ijpzm\r\na=ice-pwd:jm5puo2ab1op3vs59ca53bdk7s\r\na=fingerprint:sha-256 40:42:FB:47:87:52:BF:CB:EC:3A:DF:EB:06:DA:2D:B7:2F:59:42:10:23:7B:9D:4C:C9:58:DD:FF:A2:8F:17:67\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:video\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:96 H264/90000\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 goog-remb\r\na=fmtp:96 packetization-mode=1;profile-level-id=42e01f\r\na=ssrc:1505338584 cname:10000000b5810aac\r\na=ssrc:1 cname:trackB\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:audio\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=ssrc:697641945 cname:10000000b5810aac\r\n" + //nolint:lll unifiedPlanOffer := "v=0\r\no=- 4648475892259889561 3 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0 1\r\na=ice-ufrag:1hhfzwf0ijpzm\r\na=ice-pwd:jm5puo2ab1op3vs59ca53bdk7s\r\na=fingerprint:sha-256 40:42:FB:47:87:52:BF:CB:EC:3A:DF:EB:06:DA:2D:B7:2F:59:42:10:23:7B:9D:4C:C9:58:DD:FF:A2:8F:17:67\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:0\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:96 H264/90000\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 goog-remb\r\na=fmtp:96 packetization-mode=1;profile-level-id=42e01f\r\na=ssrc:1505338584 cname:10000000b5810aac\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=setup:passive\r\na=mid:1\r\na=sendonly\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=ssrc:697641945 cname:10000000b5810aac\r\n" report := test.CheckRoutines(t) diff --git a/sdptype.go b/sdptype.go index 5aa3b986..f265d9d6 100644 --- a/sdptype.go +++ b/sdptype.go @@ -12,7 +12,7 @@ import ( type SDPType int const ( - // SDPTypeUnknown is the enum's zero-value + // SDPTypeUnknown is the enum's zero-value. SDPTypeUnknown SDPType = iota // SDPTypeOffer indicates that a description MUST be treated as an SDP offer. @@ -46,7 +46,7 @@ const ( sdpTypeRollbackStr = "rollback" ) -// NewSDPType creates an SDPType from a string +// NewSDPType creates an SDPType from a string. func NewSDPType(raw string) SDPType { switch raw { case sdpTypeOfferStr: @@ -77,12 +77,12 @@ func (t SDPType) String() string { } } -// MarshalJSON enables JSON marshaling of a SDPType +// MarshalJSON enables JSON marshaling of a SDPType. func (t SDPType) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } -// UnmarshalJSON enables JSON unmarshaling of a SDPType +// UnmarshalJSON enables JSON unmarshaling of a SDPType. func (t *SDPType) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { diff --git a/sessiondescription.go b/sessiondescription.go index 91e22a91..186f8583 100644 --- a/sessiondescription.go +++ b/sessiondescription.go @@ -16,9 +16,10 @@ type SessionDescription struct { parsed *sdp.SessionDescription } -// Unmarshal is a helper to deserialize the sdp +// Unmarshal is a helper to deserialize the sdp. func (sd *SessionDescription) Unmarshal() (*sdp.SessionDescription, error) { sd.parsed = &sdp.SessionDescription{} err := sd.parsed.UnmarshalString(sd.SDP) + return sd.parsed, err } diff --git a/sessiondescription_test.go b/sessiondescription_test.go index c167d0d1..8214f99f 100644 --- a/sessiondescription_test.go +++ b/sessiondescription_test.go @@ -46,6 +46,7 @@ func TestSessionDescription_JSON(t *testing.T) { testCase.unmarshalErr, "testCase: %d %v", i, testCase, ) + continue } diff --git a/settingengine.go b/settingengine.go index edfb1549..c5f481b0 100644 --- a/settingengine.go +++ b/settingengine.go @@ -96,7 +96,7 @@ type SettingEngine struct { iceUDPMux ice.UDPMux iceProxyDialer proxy.Dialer iceDisableActiveTCP bool - iceBindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool + iceBindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool //nolint:lll disableMediaEngineCopy bool srtpProtectionProfiles []dtls.SRTPProtectionProfile receiveMTU uint @@ -106,7 +106,7 @@ type SettingEngine struct { dataChannelBlockWrite bool } -// getReceiveMTU returns the configured MTU. If SettingEngine's MTU is configured to 0 it returns the default +// getReceiveMTU returns the configured MTU. If SettingEngine's MTU is configured to 0 it returns the default. func (e *SettingEngine) getReceiveMTU() uint { if e.receiveMTU != 0 { return e.receiveMTU @@ -123,13 +123,13 @@ func (e *SettingEngine) DetachDataChannels() { } // EnableDataChannelBlockWrite allows data channels to block on write, -// it only works if DetachDataChannels is enabled +// it only works if DetachDataChannels is enabled. func (e *SettingEngine) EnableDataChannelBlockWrite(nonblockWrite bool) { e.dataChannelBlockWrite = nonblockWrite } // SetSRTPProtectionProfiles allows the user to override the default SRTP Protection Profiles -// The default srtp protection profiles are provided by the function `defaultSrtpProtectionProfiles` +// The default srtp protection profiles are provided by the function `defaultSrtpProtectionProfiles`. func (e *SettingEngine) SetSRTPProtectionProfiles(profiles ...dtls.SRTPProtectionProfile) { e.srtpProtectionProfiles = profiles } @@ -146,34 +146,36 @@ func (e *SettingEngine) SetSRTPProtectionProfiles(profiles ...dtls.SRTPProtectio // // keepAliveInterval: // -// How often the ICE Agent sends extra traffic if there is no activity, if media is flowing no traffic will be sent. Default is 2 seconds +// How often the ICE Agent sends extra traffic if there is no activity, if media is flowing no traffic will be sent. +// +// Default is 2 seconds. func (e *SettingEngine) SetICETimeouts(disconnectedTimeout, failedTimeout, keepAliveInterval time.Duration) { e.timeout.ICEDisconnectedTimeout = &disconnectedTimeout e.timeout.ICEFailedTimeout = &failedTimeout e.timeout.ICEKeepaliveInterval = &keepAliveInterval } -// SetHostAcceptanceMinWait sets the ICEHostAcceptanceMinWait +// SetHostAcceptanceMinWait sets the ICEHostAcceptanceMinWait. func (e *SettingEngine) SetHostAcceptanceMinWait(t time.Duration) { e.timeout.ICEHostAcceptanceMinWait = &t } -// SetSrflxAcceptanceMinWait sets the ICESrflxAcceptanceMinWait +// SetSrflxAcceptanceMinWait sets the ICESrflxAcceptanceMinWait. func (e *SettingEngine) SetSrflxAcceptanceMinWait(t time.Duration) { e.timeout.ICESrflxAcceptanceMinWait = &t } -// SetPrflxAcceptanceMinWait sets the ICEPrflxAcceptanceMinWait +// SetPrflxAcceptanceMinWait sets the ICEPrflxAcceptanceMinWait. func (e *SettingEngine) SetPrflxAcceptanceMinWait(t time.Duration) { e.timeout.ICEPrflxAcceptanceMinWait = &t } -// SetRelayAcceptanceMinWait sets the ICERelayAcceptanceMinWait +// SetRelayAcceptanceMinWait sets the ICERelayAcceptanceMinWait. func (e *SettingEngine) SetRelayAcceptanceMinWait(t time.Duration) { e.timeout.ICERelayAcceptanceMinWait = &t } -// SetSTUNGatherTimeout sets the ICESTUNGatherTimeout +// SetSTUNGatherTimeout sets the ICESTUNGatherTimeout. func (e *SettingEngine) SetSTUNGatherTimeout(t time.Duration) { e.timeout.ICESTUNGatherTimeout = &t } @@ -191,10 +193,11 @@ func (e *SettingEngine) SetEphemeralUDPPortRange(portMin, portMax uint16) error e.ephemeralUDP.PortMin = portMin e.ephemeralUDP.PortMax = portMax + return nil } -// SetLite configures whether or not the ice agent should be a lite agent +// SetLite configures whether or not the ice agent should be a lite agent. func (e *SettingEngine) SetLite(lite bool) { e.candidates.ICELite = lite } @@ -208,7 +211,7 @@ func (e *SettingEngine) SetNetworkTypes(candidateTypes []NetworkType) { // SetInterfaceFilter sets the filtering functions when gathering ICE candidates // This can be used to exclude certain network interfaces from ICE. Which may be // useful if you know a certain interface will never succeed, or if you wish to reduce -// the amount of information you wish to expose to the remote peer +// the amount of information you wish to expose to the remote peer. func (e *SettingEngine) SetInterfaceFilter(filter func(string) (keep bool)) { e.candidates.InterfaceFilter = filter } @@ -216,7 +219,7 @@ func (e *SettingEngine) SetInterfaceFilter(filter func(string) (keep bool)) { // SetIPFilter sets the filtering functions when gathering ICE candidates // This can be used to exclude certain ip from ICE. Which may be // useful if you know a certain ip will never succeed, or if you wish to reduce -// the amount of information you wish to expose to the remote peer +// the amount of information you wish to expose to the remote peer. func (e *SettingEngine) SetIPFilter(filter func(net.IP) (keep bool)) { e.candidates.IPFilter = filter } @@ -252,7 +255,7 @@ func (e *SettingEngine) SetNAT1To1IPs(ips []string, candidateType ICECandidateTy } // SetIncludeLoopbackCandidate enable pion to gather loopback candidates, it is useful -// for some VM have public IP mapped to loopback interface +// for some VM have public IP mapped to loopback interface. func (e *SettingEngine) SetIncludeLoopbackCandidate(include bool) { e.candidates.IncludeLoopbackCandidate = include } @@ -274,6 +277,7 @@ func (e *SettingEngine) SetAnsweringDTLSRole(role DTLSRole) error { } e.answeringDTLSRole = role + return nil } @@ -285,28 +289,29 @@ func (e *SettingEngine) SetNet(net transport.Net) { e.net = net } -// SetICEMulticastDNSMode controls if pion/ice queries and generates mDNS ICE Candidates +// SetICEMulticastDNSMode controls if pion/ice queries and generates mDNS ICE Candidates. func (e *SettingEngine) SetICEMulticastDNSMode(multicastDNSMode ice.MulticastDNSMode) { e.candidates.MulticastDNSMode = multicastDNSMode } // SetMulticastDNSHostName sets a static HostName to be used by pion/ice instead of generating one on startup // -// This should only be used for a single PeerConnection. Having multiple PeerConnections with the same HostName will cause -// undefined behavior +// This should only be used for a single PeerConnection. +// Having multiple PeerConnections with the same HostName will cause undefined behavior. func (e *SettingEngine) SetMulticastDNSHostName(hostName string) { e.candidates.MulticastDNSHostName = hostName } // SetICECredentials sets a staic uFrag/uPwd to be used by pion/ice // -// This is useful if you want to do signalless WebRTC session, or having a reproducible environment with static credentials +// This is useful if you want to do signalless WebRTC session, +// or having a reproducible environment with static credentials. func (e *SettingEngine) SetICECredentials(usernameFragment, password string) { e.candidates.UsernameFragment = usernameFragment e.candidates.Password = password } -// DisableCertificateFingerprintVerification disables fingerprint verification after DTLS Handshake has finished +// DisableCertificateFingerprintVerification disables fingerprint verification after DTLS Handshake has finished. func (e *SettingEngine) DisableCertificateFingerprintVerification(isDisabled bool) { e.disableCertificateFingerprintVerification = isDisabled } @@ -370,7 +375,7 @@ func (e *SettingEngine) SetICEMaxBindingRequests(d uint16) { e.iceMaxBindingRequests = &d } -// DisableActiveTCP disables using active TCP for ICE. Active TCP is enabled by default +// DisableActiveTCP disables using active TCP for ICE. Active TCP is enabled by default. func (e *SettingEngine) DisableActiveTCP(isDisabled bool) { e.iceDisableActiveTCP = isDisabled } @@ -383,7 +388,7 @@ func (e *SettingEngine) DisableMediaEngineCopy(isDisabled bool) { } // SetReceiveMTU sets the size of read buffer that copies incoming packets. This is optional. -// Leave this 0 for the default receiveMTU +// Leave this 0 for the default receiveMTU. func (e *SettingEngine) SetReceiveMTU(receiveMTU uint) { e.receiveMTU = receiveMTU } @@ -457,7 +462,7 @@ func (e *SettingEngine) SetSCTPMaxReceiveBufferSize(maxReceiveBufferSize uint32) // EnableSCTPZeroChecksum controls the zero checksum feature in SCTP. // This removes the need to checksum every incoming/outgoing packet and will reduce -// latency and CPU usage. This feature is not backwards compatible so is disabled by default +// latency and CPU usage. This feature is not backwards compatible so is disabled by default. func (e *SettingEngine) EnableSCTPZeroChecksum(isEnabled bool) { e.sctp.enableZeroChecksum = isEnabled } @@ -482,7 +487,9 @@ func (e *SettingEngine) SetDTLSServerHelloMessageHook(hook func(handshake.Messag // SetDTLSCertificateRequestMessageHook if not nil, is called when a DTLS Certificate Request message is sent // from a client. The returned handshake message replaces the original message. -func (e *SettingEngine) SetDTLSCertificateRequestMessageHook(hook func(handshake.MessageCertificateRequest) handshake.Message) { +func (e *SettingEngine) SetDTLSCertificateRequestMessageHook( + hook func(handshake.MessageCertificateRequest) handshake.Message, +) { e.dtls.certificateRequestMessageHook = hook } @@ -496,8 +503,10 @@ func (e *SettingEngine) SetSCTPRTOMax(rtoMax time.Duration) { // This allows users to do things like // - Log incoming Binding Requests for debugging // - Implement draft-thatcher-ice-renomination -// - Implement custom CandidatePair switching logic -func (e *SettingEngine) SetICEBindingRequestHandler(bindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool) { +// - Implement custom CandidatePair switching logic. +func (e *SettingEngine) SetICEBindingRequestHandler( + bindingRequestHandler func(m *stun.Message, local, remote ice.Candidate, pair *ice.CandidatePair) bool, +) { e.iceBindingRequestHandler = bindingRequestHandler } diff --git a/settingengine_test.go b/settingengine_test.go index 3496fc3a..7950600c 100644 --- a/settingengine_test.go +++ b/settingengine_test.go @@ -22,24 +22,24 @@ import ( ) func TestSetEphemeralUDPPortRange(t *testing.T) { - s := SettingEngine{} + settingEngine := SettingEngine{} - if s.ephemeralUDP.PortMin != 0 || - s.ephemeralUDP.PortMax != 0 { + if settingEngine.ephemeralUDP.PortMin != 0 || + settingEngine.ephemeralUDP.PortMax != 0 { t.Fatalf("SettingEngine defaults aren't as expected.") } // set bad ephemeral ports - if err := s.SetEphemeralUDPPortRange(3000, 2999); err == nil { + if err := settingEngine.SetEphemeralUDPPortRange(3000, 2999); err == nil { t.Fatalf("Setting engine should fail bad ephemeral ports.") } - if err := s.SetEphemeralUDPPortRange(3000, 4000); err != nil { + if err := settingEngine.SetEphemeralUDPPortRange(3000, 4000); err != nil { t.Fatalf("Setting engine failed valid port range: %s", err) } - if s.ephemeralUDP.PortMin != 3000 || - s.ephemeralUDP.PortMax != 4000 { + if settingEngine.ephemeralUDP.PortMin != 3000 || + settingEngine.ephemeralUDP.PortMax != 4000 { t.Fatalf("Setting engine ports do not reflect expected range") } } @@ -73,54 +73,62 @@ func TestDetachDataChannels(t *testing.T) { } func TestSetNAT1To1IPs(t *testing.T) { - s := SettingEngine{} - if s.candidates.NAT1To1IPs != nil { + settingEngine := SettingEngine{} + if settingEngine.candidates.NAT1To1IPs != nil { t.Errorf("Invalid default value") } - if s.candidates.NAT1To1IPCandidateType != 0 { + if settingEngine.candidates.NAT1To1IPCandidateType != 0 { t.Errorf("Invalid default value") } ips := []string{"1.2.3.4"} typ := ICECandidateTypeHost - s.SetNAT1To1IPs(ips, typ) - if len(s.candidates.NAT1To1IPs) != 1 || s.candidates.NAT1To1IPs[0] != "1.2.3.4" { + settingEngine.SetNAT1To1IPs(ips, typ) + if len(settingEngine.candidates.NAT1To1IPs) != 1 || settingEngine.candidates.NAT1To1IPs[0] != "1.2.3.4" { t.Fatalf("Failed to set NAT1To1IPs") } - if s.candidates.NAT1To1IPCandidateType != typ { + if settingEngine.candidates.NAT1To1IPCandidateType != typ { t.Fatalf("Failed to set NAT1To1IPCandidateType") } } func TestSetAnsweringDTLSRole(t *testing.T) { s := SettingEngine{} - assert.Error(t, s.SetAnsweringDTLSRole(DTLSRoleAuto), "SetAnsweringDTLSRole can only be called with DTLSRoleClient or DTLSRoleServer") - assert.Error(t, s.SetAnsweringDTLSRole(DTLSRole(0)), "SetAnsweringDTLSRole can only be called with DTLSRoleClient or DTLSRoleServer") + assert.Error( + t, + s.SetAnsweringDTLSRole(DTLSRoleAuto), + "SetAnsweringDTLSRole can only be called with DTLSRoleClient or DTLSRoleServer", + ) + assert.Error( + t, + s.SetAnsweringDTLSRole(DTLSRole(0)), + "SetAnsweringDTLSRole can only be called with DTLSRoleClient or DTLSRoleServer", + ) } func TestSetReplayProtection(t *testing.T) { - s := SettingEngine{} + settingEngine := SettingEngine{} - if s.replayProtection.DTLS != nil || - s.replayProtection.SRTP != nil || - s.replayProtection.SRTCP != nil { + if settingEngine.replayProtection.DTLS != nil || + settingEngine.replayProtection.SRTP != nil || + settingEngine.replayProtection.SRTCP != nil { t.Fatalf("SettingEngine defaults aren't as expected.") } - s.SetDTLSReplayProtectionWindow(128) - s.SetSRTPReplayProtectionWindow(64) - s.SetSRTCPReplayProtectionWindow(32) + settingEngine.SetDTLSReplayProtectionWindow(128) + settingEngine.SetSRTPReplayProtectionWindow(64) + settingEngine.SetSRTCPReplayProtectionWindow(32) - if s.replayProtection.DTLS == nil || - *s.replayProtection.DTLS != 128 { + if settingEngine.replayProtection.DTLS == nil || + *settingEngine.replayProtection.DTLS != 128 { t.Errorf("Failed to set DTLS replay protection window") } - if s.replayProtection.SRTP == nil || - *s.replayProtection.SRTP != 64 { + if settingEngine.replayProtection.SRTP == nil || + *settingEngine.replayProtection.SRTP != 64 { t.Errorf("Failed to set SRTP replay protection window") } - if s.replayProtection.SRTCP == nil || - *s.replayProtection.SRTCP != 32 { + if settingEngine.replayProtection.SRTCP == nil || + *settingEngine.replayProtection.SRTCP != 32 { t.Errorf("Failed to set SRTCP replay protection window") } } @@ -152,10 +160,10 @@ func TestSettingEngine_SetICETCP(t *testing.T) { func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) { t.Run("Copy", func(t *testing.T) { - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) - api := NewAPI(WithMediaEngine(m)) + api := NewAPI(WithMediaEngine(mediaEngine)) offerer, answerer, err := api.newPair(Configuration{}) assert.NoError(t, err) @@ -166,8 +174,8 @@ func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) { assert.NoError(t, signalPair(offerer, answerer)) // Assert that the MediaEngine the user created isn't modified - assert.False(t, m.negotiatedVideo) - assert.Empty(t, m.negotiatedVideoCodecs) + assert.False(t, mediaEngine.negotiatedVideo) + assert.Empty(t, mediaEngine.negotiatedVideoCodecs) // Assert that the internal MediaEngine is modified assert.True(t, offerer.api.mediaEngine.negotiatedVideo) @@ -190,13 +198,13 @@ func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) { }) t.Run("No Copy", func(t *testing.T) { - m := &MediaEngine{} - assert.NoError(t, m.RegisterDefaultCodecs()) + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterDefaultCodecs()) s := SettingEngine{} s.DisableMediaEngineCopy(true) - api := NewAPI(WithMediaEngine(m), WithSettingEngine(s)) + api := NewAPI(WithMediaEngine(mediaEngine), WithSettingEngine(s)) offerer, answerer, err := api.newPair(Configuration{}) assert.NoError(t, err) @@ -207,8 +215,8 @@ func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) { assert.NoError(t, signalPair(offerer, answerer)) // Assert that the user MediaEngine was modified, so no copy happened - assert.True(t, m.negotiatedVideo) - assert.NotEmpty(t, m.negotiatedVideoCodecs) + assert.True(t, mediaEngine.negotiatedVideo) + assert.NotEmpty(t, mediaEngine.negotiatedVideoCodecs) closePairNow(t, offerer, answerer) @@ -224,21 +232,21 @@ func TestSettingEngine_SetDisableMediaEngineCopy(t *testing.T) { } func TestSetDTLSRetransmissionInterval(t *testing.T) { - s := SettingEngine{} + settingEngine := SettingEngine{} - if s.dtls.retransmissionInterval != 0 { + if settingEngine.dtls.retransmissionInterval != 0 { t.Fatalf("SettingEngine defaults aren't as expected.") } - s.SetDTLSRetransmissionInterval(100 * time.Millisecond) - if s.dtls.retransmissionInterval == 0 || - s.dtls.retransmissionInterval != 100*time.Millisecond { + settingEngine.SetDTLSRetransmissionInterval(100 * time.Millisecond) + if settingEngine.dtls.retransmissionInterval == 0 || + settingEngine.dtls.retransmissionInterval != 100*time.Millisecond { t.Errorf("Failed to set DTLS retransmission interval") } - s.SetDTLSRetransmissionInterval(1 * time.Second) - if s.dtls.retransmissionInterval == 0 || - s.dtls.retransmissionInterval != 1*time.Second { + settingEngine.SetDTLSRetransmissionInterval(1 * time.Second) + if settingEngine.dtls.retransmissionInterval == 0 || + settingEngine.dtls.retransmissionInterval != 1*time.Second { t.Errorf("Failed to set DTLS retransmission interval") } } @@ -287,8 +295,8 @@ func TestSetICEBindingRequestHandler(t *testing.T) { seenICEControlled, seenICEControlledCancel := context.WithCancel(context.Background()) seenICEControlling, seenICEControllingCancel := context.WithCancel(context.Background()) - s := SettingEngine{} - s.SetICEBindingRequestHandler(func(m *stun.Message, _, _ ice.Candidate, _ *ice.CandidatePair) bool { + settingEngine := SettingEngine{} + settingEngine.SetICEBindingRequestHandler(func(m *stun.Message, _, _ ice.Candidate, _ *ice.CandidatePair) bool { for _, a := range m.Attributes { switch a.Type { case stun.AttrICEControlled: @@ -302,7 +310,7 @@ func TestSetICEBindingRequestHandler(t *testing.T) { return false }) - pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{}) + pcOffer, pcAnswer, err := NewAPI(WithSettingEngine(settingEngine)).newPair(Configuration{}) assert.NoError(t, err) assert.NoError(t, signalPair(pcOffer, pcAnswer)) @@ -313,31 +321,31 @@ func TestSetICEBindingRequestHandler(t *testing.T) { } func TestSetHooks(t *testing.T) { - s := SettingEngine{} + settingEngine := SettingEngine{} - if s.dtls.clientHelloMessageHook != nil || - s.dtls.serverHelloMessageHook != nil || - s.dtls.certificateRequestMessageHook != nil { + if settingEngine.dtls.clientHelloMessageHook != nil || + settingEngine.dtls.serverHelloMessageHook != nil || + settingEngine.dtls.certificateRequestMessageHook != nil { t.Fatalf("SettingEngine defaults aren't as expected.") } - s.SetDTLSClientHelloMessageHook(func(msg handshake.MessageClientHello) handshake.Message { + settingEngine.SetDTLSClientHelloMessageHook(func(msg handshake.MessageClientHello) handshake.Message { return &msg }) - s.SetDTLSServerHelloMessageHook(func(msg handshake.MessageServerHello) handshake.Message { + settingEngine.SetDTLSServerHelloMessageHook(func(msg handshake.MessageServerHello) handshake.Message { return &msg }) - s.SetDTLSCertificateRequestMessageHook(func(msg handshake.MessageCertificateRequest) handshake.Message { + settingEngine.SetDTLSCertificateRequestMessageHook(func(msg handshake.MessageCertificateRequest) handshake.Message { return &msg }) - if s.dtls.clientHelloMessageHook == nil { + if settingEngine.dtls.clientHelloMessageHook == nil { t.Errorf("Failed to set DTLS Client Hello Hook") } - if s.dtls.serverHelloMessageHook == nil { + if settingEngine.dtls.serverHelloMessageHook == nil { t.Errorf("Failed to set DTLS Server Hello Hook") } - if s.dtls.certificateRequestMessageHook == nil { + if settingEngine.dtls.certificateRequestMessageHook == nil { t.Errorf("Failed to set DTLS Certificate Request Hook") } } @@ -349,22 +357,36 @@ func TestSetFireOnTrackBeforeFirstRTP(t *testing.T) { report := test.CheckRoutines(t) defer report() - s := SettingEngine{} - s.SetFireOnTrackBeforeFirstRTP(true) + settingEngine := SettingEngine{} + settingEngine.SetFireOnTrackBeforeFirstRTP(true) mediaEngineOne := &MediaEngine{} assert.NoError(t, mediaEngineOne.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 100, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 100, }, RTPCodecTypeVideo)) mediaEngineTwo := &MediaEngine{} assert.NoError(t, mediaEngineTwo.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 200, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 200, }, RTPCodecTypeVideo)) - offerer, err := NewAPI(WithMediaEngine(mediaEngineOne), WithSettingEngine(s)).NewPeerConnection(Configuration{}) + offerer, err := NewAPI(WithMediaEngine(mediaEngineOne), WithSettingEngine(settingEngine)).NewPeerConnection( + Configuration{}, + ) assert.NoError(t, err) answerer, err := NewAPI(WithMediaEngine(mediaEngineTwo)).NewPeerConnection(Configuration{}) @@ -391,7 +413,7 @@ func TestSetFireOnTrackBeforeFirstRTP(t *testing.T) { assert.NoError(t, signalPair(offerer, answerer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{track}) closePairNow(t, offerer, answerer) } diff --git a/signalingstate.go b/signalingstate.go index 9d60afd2..03b8fb23 100644 --- a/signalingstate.go +++ b/signalingstate.go @@ -32,7 +32,7 @@ func (op stateChangeOp) String() string { type SignalingState int32 const ( - // SignalingStateUnknown is the enum's zero-value + // SignalingStateUnknown is the enum's zero-value. SignalingStateUnknown SignalingState = iota // SignalingStateStable indicates there is no offer/answer exchange in @@ -110,17 +110,18 @@ func (t SignalingState) String() string { } } -// Get thread safe read value +// Get thread safe read value. func (t *SignalingState) Get() SignalingState { return SignalingState(atomic.LoadInt32((*int32)(t))) } -// Set thread safe write value +// Set thread safe write value. func (t *SignalingState) Set(state SignalingState) { atomic.StoreInt32((*int32)(t), int32(state)) } -func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType SDPType) (SignalingState, error) { // nolint:gocognit +//nolint:gocognit,cyclop +func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType SDPType) (SignalingState, error) { // Special case for rollbacks if sdpType == SDPTypeRollback && cur == SignalingStateStable { return cur, &rtcerr.InvalidModificationError{ @@ -188,6 +189,7 @@ func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType } } } + return cur, &rtcerr.InvalidModificationError{ Err: fmt.Errorf("%w: %s->%s(%s)->%s", errSignalingStateProposedTransitionInvalid, cur, op, sdpType, next), } diff --git a/srtp_writer_future.go b/srtp_writer_future.go index fe090858..31afb2d1 100644 --- a/srtp_writer_future.go +++ b/srtp_writer_future.go @@ -17,7 +17,7 @@ import ( ) // srtpWriterFuture blocks Read/Write calls until -// the SRTP Session is available +// the SRTP Session is available. type srtpWriterFuture struct { ssrc SSRC rtpSender *RTPSender @@ -27,7 +27,7 @@ type srtpWriterFuture struct { closed bool } -func (s *srtpWriterFuture) init(returnWhenNoSRTP bool) error { +func (s *srtpWriterFuture) init(returnWhenNoSRTP bool) error { //nolint:cyclop if returnWhenNoSRTP { select { case <-s.rtpSender.stopCalled: @@ -73,6 +73,7 @@ func (s *srtpWriterFuture) init(returnWhenNoSRTP bool) error { s.rtcpReadStream.Store(rtcpReadStream) s.rtpWriteStream.Store(rtpWriteStream) + return nil } diff --git a/stats.go b/stats.go index 4dfa3bad..0d73394b 100644 --- a/stats.go +++ b/stats.go @@ -18,8 +18,8 @@ type Stats interface { statsMarker() } -// UnmarshalStatsJSON unmarshals a Stats object from JSON -func UnmarshalStatsJSON(b []byte) (Stats, error) { +// UnmarshalStatsJSON unmarshals a Stats object from JSON. +func UnmarshalStatsJSON(b []byte) (Stats, error) { //nolint:cyclop type typeJSON struct { Type StatsType `json:"type"` } @@ -135,17 +135,17 @@ const ( // StatsTypeCertificate is used by CertificateStats. StatsTypeCertificate StatsType = "certificate" - // StatsTypeSCTPTransport is used by SCTPTransportStats + // StatsTypeSCTPTransport is used by SCTPTransportStats. StatsTypeSCTPTransport StatsType = "sctp-transport" ) -// MediaKind indicates the kind of media (audio or video) +// MediaKind indicates the kind of media (audio or video). type MediaKind string const ( - // MediaKindAudio indicates this is audio stats + // MediaKindAudio indicates this is audio stats. MediaKindAudio MediaKind = "audio" - // MediaKindVideo indicates this is video stats + // MediaKindVideo indicates this is video stats. MediaKindVideo MediaKind = "video" ) @@ -202,11 +202,12 @@ func (src *statsReportCollector) Ready() StatsReport { src.collectingGroup.Wait() src.mux.Lock() defer src.mux.Unlock() + return src.report } // CodecType specifies whether a CodecStats objects represents a media format -// that is being encoded or decoded +// that is being encoded or decoded. type CodecType string const ( @@ -269,6 +270,7 @@ func unmarshalCodecStats(b []byte) (CodecStats, error) { if err != nil { return CodecStats{}, fmt.Errorf("unmarshal codec stats: %w", err) } + return codecStats, nil } @@ -605,6 +607,7 @@ func unmarshalInboundRTPStreamStats(b []byte) (InboundRTPStreamStats, error) { if err != nil { return InboundRTPStreamStats{}, fmt.Errorf("unmarshal inbound rtp stream stats: %w", err) } + return inboundRTPStreamStats, nil } @@ -619,10 +622,14 @@ const ( // QualityLimitationReasonCPU means the resolution and/or framerate is primarily limited due to CPU load. QualityLimitationReasonCPU QualityLimitationReason = "cpu" - // QualityLimitationReasonBandwidth means the resolution and/or framerate is primarily limited due to congestion cues during bandwidth estimation. Typical, congestion control algorithms use inter-arrival time, round-trip time, packet or other congestion cues to perform bandwidth estimation. + // QualityLimitationReasonBandwidth means the resolution and/or framerate is primarily limited + // due to congestion cues during bandwidth estimation. + // Typical, congestion control algorithms use inter-arrival time, round-trip time, + // packet or other congestion cues to perform bandwidth estimation. QualityLimitationReasonBandwidth QualityLimitationReason = "bandwidth" - // QualityLimitationReasonOther means the resolution and/or framerate is primarily limited for a reason other than the above. + // QualityLimitationReasonOther means the resolution and/or framerate is primarily limited + // for a reason other than the above. QualityLimitationReasonOther QualityLimitationReason = "other" ) @@ -855,6 +862,7 @@ func unmarshalOutboundRTPStreamStats(b []byte) (OutboundRTPStreamStats, error) { if err != nil { return OutboundRTPStreamStats{}, fmt.Errorf("unmarshal outbound rtp stream stats: %w", err) } + return outboundRTPStreamStats, nil } @@ -986,6 +994,7 @@ func unmarshalRemoteInboundRTPStreamStats(b []byte) (RemoteInboundRTPStreamStats if err != nil { return RemoteInboundRTPStreamStats{}, fmt.Errorf("unmarshal remote inbound rtp stream stats: %w", err) } + return remoteInboundRTPStreamStats, nil } @@ -1105,6 +1114,7 @@ func unmarshalRemoteOutboundRTPStreamStats(b []byte) (RemoteOutboundRTPStreamSta if err != nil { return RemoteOutboundRTPStreamStats{}, fmt.Errorf("unmarshal remote outbound rtp stream stats: %w", err) } + return remoteOutboundRTPStreamStats, nil } @@ -1152,6 +1162,7 @@ func unmarshalCSRCStats(b []byte) (RTPContributingSourceStats, error) { if err != nil { return RTPContributingSourceStats{}, fmt.Errorf("unmarshal csrc stats: %w", err) } + return csrcStats, nil } @@ -1215,7 +1226,8 @@ type AudioSourceStats struct { // TotalCaptureDelay is the total delay, in seconds, for each audio sample between the time the sample was emitted // by the capture device and the sample reaching the source. This can be used together with totalSamplesCaptured to - // calculate the average capture delay per sample. Only applicable if the audio source represents an audio capture device. + // calculate the average capture delay per sample. + // Only applicable if the audio source represents an audio capture device. TotalCaptureDelay float64 `json:"totalCaptureDelay"` // TotalSamplesCaptured is the total number of captured samples reaching the audio source, i.e. that were not dropped @@ -1278,6 +1290,7 @@ func unmarshalMediaSourceStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal audio source stats: %w", err) } + return mediaSourceStats, nil case MediaKindVideo: var mediaSourceStats VideoSourceStats @@ -1285,6 +1298,7 @@ func unmarshalMediaSourceStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal video source stats: %w", err) } + return mediaSourceStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) @@ -1346,6 +1360,7 @@ func unmarshalMediaPlayoutStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal audio playout stats: %w", err) } + return audioPlayoutStats, nil } @@ -1391,6 +1406,7 @@ func unmarshalPeerConnectionStats(b []byte) (PeerConnectionStats, error) { if err != nil { return PeerConnectionStats{}, fmt.Errorf("unmarshal pc stats: %w", err) } + return pcStats, nil } @@ -1445,6 +1461,7 @@ func unmarshalDataChannelStats(b []byte) (DataChannelStats, error) { if err != nil { return DataChannelStats{}, fmt.Errorf("unmarshal data channel stats: %w", err) } + return dataChannelStats, nil } @@ -1477,6 +1494,7 @@ func unmarshalStreamStats(b []byte) (MediaStreamStats, error) { if err != nil { return MediaStreamStats{}, fmt.Errorf("unmarshal stream stats: %w", err) } + return streamStats, nil } @@ -1658,6 +1676,7 @@ func unmarshalSenderStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal audio sender stats: %w", err) } + return senderStats, nil case MediaKindVideo: var senderStats VideoSenderStats @@ -1665,6 +1684,7 @@ func unmarshalSenderStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal video sender stats: %w", err) } + return senderStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) @@ -1689,6 +1709,7 @@ func unmarshalTrackStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal audio track stats: %w", err) } + return trackStats, nil case MediaKindVideo: var trackStats SenderVideoTrackAttachmentStats @@ -1696,6 +1717,7 @@ func unmarshalTrackStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal video track stats: %w", err) } + return trackStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) @@ -1894,6 +1916,7 @@ func unmarshalReceiverStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal audio receiver stats: %w", err) } + return receiverStats, nil case MediaKindVideo: var receiverStats VideoReceiverStats @@ -1901,6 +1924,7 @@ func unmarshalReceiverStats(b []byte) (Stats, error) { if err != nil { return nil, fmt.Errorf("unmarshal video receiver stats: %w", err) } + return receiverStats, nil default: return nil, fmt.Errorf("kind: %w", ErrUnknownType) @@ -1980,6 +2004,7 @@ func unmarshalTransportStats(b []byte) (TransportStats, error) { if err != nil { return TransportStats{}, fmt.Errorf("unmarshal transport stats: %w", err) } + return transportStats, nil } @@ -2000,6 +2025,7 @@ func toStatsICECandidatePairState(state ice.CandidatePairState) (StatsICECandida default: // NOTE: this should never happen[tm] err := fmt.Errorf("%w: %s", errStatsICECandidateStateInvalid, state.String()) + return StatsICECandidatePairState("Unknown"), err } } @@ -2228,6 +2254,7 @@ func unmarshalICECandidatePairStats(b []byte) (ICECandidatePairStats, error) { if err != nil { return ICECandidatePairStats{}, fmt.Errorf("unmarshal ice candidate pair stats: %w", err) } + return iceCandidatePairStats, nil } @@ -2305,6 +2332,7 @@ func unmarshalICECandidateStats(b []byte) (ICECandidateStats, error) { if err != nil { return ICECandidateStats{}, fmt.Errorf("unmarshal ice candidate stats: %w", err) } + return iceCandidateStats, nil } @@ -2344,6 +2372,7 @@ func unmarshalCertificateStats(b []byte) (CertificateStats, error) { if err != nil { return CertificateStats{}, fmt.Errorf("unmarshal certificate stats: %w", err) } + return certificateStats, nil } @@ -2364,8 +2393,9 @@ type SCTPTransportStats struct { // RTCTransportStats for the DTLSTransport and ICETransport supporting the SCTP transport. TransportID string `json:"transportId"` - // SmoothedRoundTripTime is the latest smoothed round-trip time value, corresponding to spinfo_srtt defined in [RFC6458] - // but converted to seconds. If there has been no round-trip time measurements yet, this value is undefined. + // SmoothedRoundTripTime is the latest smoothed round-trip time value, + // corresponding to spinfo_srtt defined in [RFC6458] but converted to seconds. + // If there has been no round-trip time measurements yet, this value is undefined. SmoothedRoundTripTime float64 `json:"smoothedRoundTripTime"` // CongestionWindow is the latest congestion window, corresponding to spinfo_cwnd defined in [RFC6458]. @@ -2394,5 +2424,6 @@ func unmarshalSCTPTransportStats(b []byte) (SCTPTransportStats, error) { if err := json.Unmarshal(b, &sctpTransportStats); err != nil { return SCTPTransportStats{}, fmt.Errorf("unmarshal sctp transport stats: %w", err) } + return sctpTransportStats, nil } diff --git a/stats_go.go b/stats_go.go index a9a8e215..58722c18 100644 --- a/stats_go.go +++ b/stats_go.go @@ -6,7 +6,7 @@ package webrtc -// GetConnectionStats is a helper method to return the associated stats for a given PeerConnection +// GetConnectionStats is a helper method to return the associated stats for a given PeerConnection. func (r StatsReport) GetConnectionStats(conn *PeerConnection) (PeerConnectionStats, bool) { statsID := conn.getStatsID() stats, ok := r[statsID] @@ -18,10 +18,11 @@ func (r StatsReport) GetConnectionStats(conn *PeerConnection) (PeerConnectionSta if !ok { return PeerConnectionStats{}, false } + return pcStats, true } -// GetDataChannelStats is a helper method to return the associated stats for a given DataChannel +// GetDataChannelStats is a helper method to return the associated stats for a given DataChannel. func (r StatsReport) GetDataChannelStats(dc *DataChannel) (DataChannelStats, bool) { statsID := dc.getStatsID() stats, ok := r[statsID] @@ -33,10 +34,11 @@ func (r StatsReport) GetDataChannelStats(dc *DataChannel) (DataChannelStats, boo if !ok { return DataChannelStats{}, false } + return dcStats, true } -// GetICECandidateStats is a helper method to return the associated stats for a given ICECandidate +// GetICECandidateStats is a helper method to return the associated stats for a given ICECandidate. func (r StatsReport) GetICECandidateStats(c *ICECandidate) (ICECandidateStats, bool) { statsID := c.statsID stats, ok := r[statsID] @@ -48,10 +50,11 @@ func (r StatsReport) GetICECandidateStats(c *ICECandidate) (ICECandidateStats, b if !ok { return ICECandidateStats{}, false } + return candidateStats, true } -// GetICECandidatePairStats is a helper method to return the associated stats for a given ICECandidatePair +// GetICECandidatePairStats is a helper method to return the associated stats for a given ICECandidatePair. func (r StatsReport) GetICECandidatePairStats(c *ICECandidatePair) (ICECandidatePairStats, bool) { statsID := c.statsID stats, ok := r[statsID] @@ -63,10 +66,11 @@ func (r StatsReport) GetICECandidatePairStats(c *ICECandidatePair) (ICECandidate if !ok { return ICECandidatePairStats{}, false } + return candidateStats, true } -// GetCertificateStats is a helper method to return the associated stats for a given Certificate +// GetCertificateStats is a helper method to return the associated stats for a given Certificate. func (r StatsReport) GetCertificateStats(c *Certificate) (CertificateStats, bool) { statsID := c.statsID stats, ok := r[statsID] @@ -78,10 +82,11 @@ func (r StatsReport) GetCertificateStats(c *Certificate) (CertificateStats, bool if !ok { return CertificateStats{}, false } + return certificateStats, true } -// GetCodecStats is a helper method to return the associated stats for a given Codec +// GetCodecStats is a helper method to return the associated stats for a given Codec. func (r StatsReport) GetCodecStats(c *RTPCodecParameters) (CodecStats, bool) { statsID := c.statsID stats, ok := r[statsID] @@ -93,5 +98,6 @@ func (r StatsReport) GetCodecStats(c *RTPCodecParameters) (CodecStats, bool) { if !ok { return CodecStats{}, false } + return codecStats, true } diff --git a/stats_go_test.go b/stats_go_test.go index 576b2c48..d5eb0b56 100644 --- a/stats_go_test.go +++ b/stats_go_test.go @@ -50,7 +50,7 @@ type statSample struct { json string } -func getStatsSamples() []statSample { +func getStatsSamples() []statSample { //nolint:cyclop,maintidx codecStats := CodecStats{ Timestamp: 1688978831527.718, Type: StatsTypeCodec, @@ -824,11 +824,14 @@ func getStatsSamples() []statSample { DTLSState: DTLSTransportStateConnected, ICEState: ICETransportStateConnected, SelectedCandidatePairID: "CPxIhBDNnT_sPDhy1TB", - LocalCertificateID: "CFF4:4F:C4:C7:F3:31:6C:B9:D5:AD:19:64:05:9F:2F:E9:00:70:56:1E:BA:92:29:3A:08:CE:1B:27:CF:2D:AB:24", - RemoteCertificateID: "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", - DTLSCipher: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - SRTPCipher: "AES_CM_128_HMAC_SHA1_80", + //nolint:lll + LocalCertificateID: "CFF4:4F:C4:C7:F3:31:6C:B9:D5:AD:19:64:05:9F:2F:E9:00:70:56:1E:BA:92:29:3A:08:CE:1B:27:CF:2D:AB:24", + //nolint:lll + RemoteCertificateID: "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", + DTLSCipher: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + SRTPCipher: "AES_CM_128_HMAC_SHA1_80", } + //nolint:lll transportStatsJSON := ` { "timestamp": 1688978831527.718, @@ -982,14 +985,19 @@ func getStatsSamples() []statSample { } ` certificateStats := CertificateStats{ - Timestamp: 1689668364374.479, - Type: StatsTypeCertificate, - ID: "CF23:AB:FA:0B:0E:DF:12:34:D3:6C:EA:83:43:BD:79:39:87:39:11:49:41:8A:63:0E:17:B1:3F:94:FA:E3:62:20", + Timestamp: 1689668364374.479, + Type: StatsTypeCertificate, + //nolint:lll + ID: "CF23:AB:FA:0B:0E:DF:12:34:D3:6C:EA:83:43:BD:79:39:87:39:11:49:41:8A:63:0E:17:B1:3F:94:FA:E3:62:20", + //nolint:lll Fingerprint: "23:AB:FA:0B:0E:DF:12:34:D3:6C:EA:83:43:BD:79:39:87:39:11:49:41:8A:63:0E:17:B1:3F:94:FA:E3:62:20", FingerprintAlgorithm: "sha-256", - Base64Certificate: "MIIBFjCBvKADAgECAggAwlrxojpmgTAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZXZWJSVEMwHhcNMjMwNzE3MDgxODU2WhcNMjMwODE3MDgxODU2WjARMQ8wDQYDVQQDDAZXZWJSVEMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARKETeS9qNGe3ltwp+q2KgsYWsJLFCJGap4L2aa862sPijHeuzLgO2bju/mosJN0Li7mXhuKBOsCkCMU7vZHVVVMAoGCCqGSM49BAMCA0kAMEYCIQDXyuyMMrgzd+w3c4h3vPn9AzLcf9CHVHRGYyy5ReI/hgIhALkXfaZ96TQRf5FI2mBJJUX9O/q4Poe3wNZxxWeDcYN+", - IssuerCertificateID: "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", + //nolint:lll + Base64Certificate: "MIIBFjCBvKADAgECAggAwlrxojpmgTAKBggqhkjOPQQDAjARMQ8wDQYDVQQDDAZXZWJSVEMwHhcNMjMwNzE3MDgxODU2WhcNMjMwODE3MDgxODU2WjARMQ8wDQYDVQQDDAZXZWJSVEMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARKETeS9qNGe3ltwp+q2KgsYWsJLFCJGap4L2aa862sPijHeuzLgO2bju/mosJN0Li7mXhuKBOsCkCMU7vZHVVVMAoGCCqGSM49BAMCA0kAMEYCIQDXyuyMMrgzd+w3c4h3vPn9AzLcf9CHVHRGYyy5ReI/hgIhALkXfaZ96TQRf5FI2mBJJUX9O/q4Poe3wNZxxWeDcYN+", + //nolint:lll + IssuerCertificateID: "CF62:AF:88:F7:F3:0F:D6:C4:93:91:1E:AD:52:F0:A4:12:04:F9:48:E7:06:16:BA:A3:86:26:8F:1E:38:1C:48:49", } + //nolint:lll certificateStatsJSON := ` { "timestamp": 1689668364374.479, @@ -1144,6 +1152,8 @@ func TestStatsUnmarshal(t *testing.T) { } func waitWithTimeout(t *testing.T, wg *sync.WaitGroup) { + t.Helper() + // Wait for all of the event handlers to be triggered. done := make(chan struct{}) go func() { @@ -1160,48 +1170,66 @@ func waitWithTimeout(t *testing.T, wg *sync.WaitGroup) { } func getConnectionStats(t *testing.T, report StatsReport, pc *PeerConnection) PeerConnectionStats { + t.Helper() + stats, ok := report.GetConnectionStats(pc) assert.True(t, ok) assert.Equal(t, stats.Type, StatsTypePeerConnection) + return stats } func getDataChannelStats(t *testing.T, report StatsReport, dc *DataChannel) DataChannelStats { + t.Helper() + stats, ok := report.GetDataChannelStats(dc) assert.True(t, ok) assert.Equal(t, stats.Type, StatsTypeDataChannel) + return stats } func getCodecStats(t *testing.T, report StatsReport, c *RTPCodecParameters) CodecStats { + t.Helper() + stats, ok := report.GetCodecStats(c) assert.True(t, ok) assert.Equal(t, stats.Type, StatsTypeCodec) + return stats } func getTransportStats(t *testing.T, report StatsReport, statsID string) TransportStats { + t.Helper() + stats, ok := report[statsID] assert.True(t, ok) transportStats, ok := stats.(TransportStats) assert.True(t, ok) assert.Equal(t, transportStats.Type, StatsTypeTransport) + return transportStats } func getSctpTransportStats(t *testing.T, report StatsReport) SCTPTransportStats { + t.Helper() + stats, ok := report["sctpTransport"] assert.True(t, ok) transportStats, ok := stats.(SCTPTransportStats) assert.True(t, ok) assert.Equal(t, transportStats.Type, StatsTypeSCTPTransport) + return transportStats } func getCertificateStats(t *testing.T, report StatsReport, certificate *Certificate) CertificateStats { + t.Helper() + certificateStats, ok := report.GetCertificateStats(certificate) assert.True(t, ok) assert.Equal(t, certificateStats.Type, StatsTypeCertificate) + return certificateStats } @@ -1213,6 +1241,7 @@ func findLocalCandidateStats(report StatsReport) []ICECandidateStats { result = append(result, stats) } } + return result } @@ -1224,10 +1253,13 @@ func findRemoteCandidateStats(report StatsReport) []ICECandidateStats { result = append(result, stats) } } + return result } func findCandidatePairStats(t *testing.T, report StatsReport) []ICECandidatePairStats { + t.Helper() + result := []ICECandidatePairStats{} for _, s := range report { stats, ok := s.(ICECandidatePairStats) @@ -1236,6 +1268,7 @@ func findCandidatePairStats(t *testing.T, report StatsReport) []ICECandidatePair result = append(result, stats) } } + return result } @@ -1277,6 +1310,7 @@ func signalPairForStats(pcOffer *PeerConnection, pcAnswer *PeerConnection) error if err != nil { return err } + return nil } } diff --git a/track_local.go b/track_local.go index 86a6b7f8..448b98af 100644 --- a/track_local.go +++ b/track_local.go @@ -8,7 +8,7 @@ import ( "github.com/pion/rtp" ) -// TrackLocalWriter is the Writer for outbound RTP Packets +// TrackLocalWriter is the Writer for outbound RTP Packets. type TrackLocalWriter interface { // WriteRTP encrypts a RTP packet and writes to the connection WriteRTP(header *rtp.Header, payload []byte) (int, error) @@ -57,39 +57,39 @@ type baseTrackLocalContext struct { } // CodecParameters returns the negotiated RTPCodecParameters. These are the codecs supported by both -// PeerConnections and the SSRC/PayloadTypes +// PeerConnections and the SSRC/PayloadTypes. func (t *baseTrackLocalContext) CodecParameters() []RTPCodecParameters { return t.params.Codecs } // HeaderExtensions returns the negotiated RTPHeaderExtensionParameters. These are the header extensions supported by -// both PeerConnections and the SSRC/PayloadTypes +// both PeerConnections and the SSRC/PayloadTypes. func (t *baseTrackLocalContext) HeaderExtensions() []RTPHeaderExtensionParameter { return t.params.HeaderExtensions } -// SSRC requires the negotiated SSRC of this track +// SSRC requires the negotiated SSRC of this track. func (t *baseTrackLocalContext) SSRC() SSRC { return t.ssrc } -// SSRCRetransmission returns the negotiated SSRC used to send retransmissions for this track +// SSRCRetransmission returns the negotiated SSRC used to send retransmissions for this track. func (t *baseTrackLocalContext) SSRCRetransmission() SSRC { return t.ssrcRTX } -// SSRCForwardErrorCorrection returns the negotiated SSRC to send forward error correction for this track +// SSRCForwardErrorCorrection returns the negotiated SSRC to send forward error correction for this track. func (t *baseTrackLocalContext) SSRCForwardErrorCorrection() SSRC { return t.ssrcFEC } // WriteStream returns the WriteStream for this TrackLocal. The implementer writes the outbound -// media packets to it +// media packets to it. func (t *baseTrackLocalContext) WriteStream() TrackLocalWriter { return t.writeStream } -// ID is a unique identifier that is used for both Bind/Unbind +// ID is a unique identifier that is used for both Bind/Unbind. func (t *baseTrackLocalContext) ID() string { return t.id } @@ -101,7 +101,7 @@ func (t *baseTrackLocalContext) RTCPReader() interceptor.RTCPReader { // TrackLocal is an interface that controls how the user can send media // The user can provide their own TrackLocal implementations, or use -// the implementations in pkg/media +// the implementations in pkg/media. type TrackLocal interface { // Bind should implement the way how the media data flows from the Track to the PeerConnection // This will be called internally after signaling is complete and the list of available diff --git a/track_local_static.go b/track_local_static.go index 2dd665cd..7512599f 100644 --- a/track_local_static.go +++ b/track_local_static.go @@ -17,7 +17,7 @@ import ( // trackBinding is a single bind for a Track // Bind can be called multiple times, this stores the -// result for a single bind call so that it can be used when writing +// result for a single bind call so that it can be used when writing. type trackBinding struct { id string ssrc, ssrcRTX, ssrcFEC SSRC @@ -26,7 +26,7 @@ type trackBinding struct { } // TrackLocalStaticRTP is a TrackLocal that has a pre-set codec and accepts RTP Packets. -// If you wish to send a media.Sample use TrackLocalStaticSample +// If you wish to send a media.Sample use TrackLocalStaticSample. type TrackLocalStaticRTP struct { mu sync.RWMutex bindings []trackBinding @@ -36,7 +36,11 @@ type TrackLocalStaticRTP struct { } // NewTrackLocalStaticRTP returns a TrackLocalStaticRTP. -func NewTrackLocalStaticRTP(c RTPCodecCapability, id, streamID string, options ...func(*TrackLocalStaticRTP)) (*TrackLocalStaticRTP, error) { +func NewTrackLocalStaticRTP( + c RTPCodecCapability, + id, streamID string, + options ...func(*TrackLocalStaticRTP), +) (*TrackLocalStaticRTP, error) { t := &TrackLocalStaticRTP{ codec: c, bindings: []trackBinding{}, @@ -58,7 +62,7 @@ func WithRTPStreamID(rid string) func(*TrackLocalStaticRTP) { } } -// WithPayloader allows the user to override the Payloader +// WithPayloader allows the user to override the Payloader. func WithPayloader(h func(RTPCodecCapability) (rtp.Payloader, error)) func(*TrackLocalStaticRTP) { return func(s *TrackLocalStaticRTP) { s.payloader = h @@ -67,21 +71,24 @@ func WithPayloader(h func(RTPCodecCapability) (rtp.Payloader, error)) func(*Trac // Bind is called by the PeerConnection after negotiation is complete // This asserts that the code requested is supported by the remote peer. -// If so it sets up all the state (SSRC and PayloadType) to have a call -func (s *TrackLocalStaticRTP) Bind(t TrackLocalContext) (RTPCodecParameters, error) { +// If so it sets up all the state (SSRC and PayloadType) to have a call. +func (s *TrackLocalStaticRTP) Bind(trackContext TrackLocalContext) (RTPCodecParameters, error) { s.mu.Lock() defer s.mu.Unlock() parameters := RTPCodecParameters{RTPCodecCapability: s.codec} - if codec, matchType := codecParametersFuzzySearch(parameters, t.CodecParameters()); matchType != codecMatchNone { + if codec, matchType := codecParametersFuzzySearch( + parameters, + trackContext.CodecParameters(), + ); matchType != codecMatchNone { s.bindings = append(s.bindings, trackBinding{ - ssrc: t.SSRC(), - ssrcRTX: t.SSRCRetransmission(), - ssrcFEC: t.SSRCForwardErrorCorrection(), + ssrc: trackContext.SSRC(), + ssrcRTX: trackContext.SSRCRetransmission(), + ssrcFEC: trackContext.SSRCForwardErrorCorrection(), payloadType: codec.PayloadType, - payloadTypeRTX: findRTXPayloadType(codec.PayloadType, t.CodecParameters()), - writeStream: t.WriteStream(), - id: t.ID(), + payloadTypeRTX: findRTXPayloadType(codec.PayloadType, trackContext.CodecParameters()), + writeStream: trackContext.WriteStream(), + id: trackContext.ID(), }) return codec, nil @@ -100,6 +107,7 @@ func (s *TrackLocalStaticRTP) Unbind(t TrackLocalContext) error { if s.bindings[i].id == t.ID() { s.bindings[i] = s.bindings[len(s.bindings)-1] s.bindings = s.bindings[:len(s.bindings)-1] + return nil } } @@ -109,16 +117,16 @@ func (s *TrackLocalStaticRTP) Unbind(t TrackLocalContext) error { // ID is the unique identifier for this Track. This should be unique for the // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video' -// and StreamID would be 'desktop' or 'webcam' +// and StreamID would be 'desktop' or 'webcam'. func (s *TrackLocalStaticRTP) ID() string { return s.id } -// StreamID is the group this track belongs too. This must be unique +// StreamID is the group this track belongs too. This must be unique. func (s *TrackLocalStaticRTP) StreamID() string { return s.streamID } // RID is the RTP stream identifier. func (s *TrackLocalStaticRTP) RID() string { return s.rid } -// Kind controls if this TrackLocal is audio or video +// Kind controls if this TrackLocal is audio or video. func (s *TrackLocalStaticRTP) Kind() RTPCodecType { switch { case strings.HasPrefix(s.codec.MimeType, "audio/"): @@ -130,7 +138,7 @@ func (s *TrackLocalStaticRTP) Kind() RTPCodecType { } } -// Codec gets the Codec of the track +// Codec gets the Codec of the track. func (s *TrackLocalStaticRTP) Codec() RTPCodecCapability { return s.codec } @@ -150,13 +158,14 @@ func resetPacketPoolAllocation(localPacket *rtp.Packet) { func getPacketAllocationFromPool() *rtp.Packet { ipacket := rtpPacketPool.Get() + return ipacket.(*rtp.Packet) //nolint:forcetypeassert } // WriteRTP writes a RTP Packet to the TrackLocalStaticRTP // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed -// PeerConnections so you can remove them +// PeerConnections so you can remove them. func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error { packet := getPacketAllocationFromPool() @@ -167,17 +176,17 @@ func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error { return s.writeRTP(packet) } -// writeRTP is like WriteRTP, except that it may modify the packet p -func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error { +// writeRTP is like WriteRTP, except that it may modify the packet p. +func (s *TrackLocalStaticRTP) writeRTP(packet *rtp.Packet) error { s.mu.RLock() defer s.mu.RUnlock() writeErrs := []error{} for _, b := range s.bindings { - p.Header.SSRC = uint32(b.ssrc) - p.Header.PayloadType = uint8(b.payloadType) - if _, err := b.writeStream.WriteRTP(&p.Header, p.Payload); err != nil { + packet.Header.SSRC = uint32(b.ssrc) + packet.Header.PayloadType = uint8(b.payloadType) + if _, err := b.writeStream.WriteRTP(&packet.Header, packet.Payload); err != nil { writeErrs = append(writeErrs, err) } } @@ -188,7 +197,7 @@ func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error { // Write writes a RTP Packet as a buffer to the TrackLocalStaticRTP // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed -// PeerConnections so you can remove them +// PeerConnections so you can remove them. func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) { packet := getPacketAllocationFromPool() @@ -202,7 +211,7 @@ func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) { } // TrackLocalStaticSample is a TrackLocal that has a pre-set codec and accepts Samples. -// If you wish to send a RTP Packet use TrackLocalStaticRTP +// If you wish to send a RTP Packet use TrackLocalStaticRTP. type TrackLocalStaticSample struct { packetizer rtp.Packetizer sequencer rtp.Sequencer @@ -210,8 +219,12 @@ type TrackLocalStaticSample struct { clockRate float64 } -// NewTrackLocalStaticSample returns a TrackLocalStaticSample -func NewTrackLocalStaticSample(c RTPCodecCapability, id, streamID string, options ...func(*TrackLocalStaticRTP)) (*TrackLocalStaticSample, error) { +// NewTrackLocalStaticSample returns a TrackLocalStaticSample. +func NewTrackLocalStaticSample( + c RTPCodecCapability, + id, streamID string, + options ...func(*TrackLocalStaticRTP), +) (*TrackLocalStaticSample, error) { rtpTrack, err := NewTrackLocalStaticRTP(c, id, streamID, options...) if err != nil { return nil, err @@ -224,26 +237,26 @@ func NewTrackLocalStaticSample(c RTPCodecCapability, id, streamID string, option // ID is the unique identifier for this Track. This should be unique for the // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video' -// and StreamID would be 'desktop' or 'webcam' +// and StreamID would be 'desktop' or 'webcam'. func (s *TrackLocalStaticSample) ID() string { return s.rtpTrack.ID() } -// StreamID is the group this track belongs too. This must be unique +// StreamID is the group this track belongs too. This must be unique. func (s *TrackLocalStaticSample) StreamID() string { return s.rtpTrack.StreamID() } // RID is the RTP stream identifier. func (s *TrackLocalStaticSample) RID() string { return s.rtpTrack.RID() } -// Kind controls if this TrackLocal is audio or video +// Kind controls if this TrackLocal is audio or video. func (s *TrackLocalStaticSample) Kind() RTPCodecType { return s.rtpTrack.Kind() } -// Codec gets the Codec of the track +// Codec gets the Codec of the track. func (s *TrackLocalStaticSample) Codec() RTPCodecCapability { return s.rtpTrack.Codec() } // Bind is called by the PeerConnection after negotiation is complete // This asserts that the code requested is supported by the remote peer. -// If so it setups all the state (SSRC and PayloadType) to have a call +// If so it setups all the state (SSRC and PayloadType) to have a call. func (s *TrackLocalStaticSample) Bind(t TrackLocalContext) (RTPCodecParameters, error) { codec, err := s.rtpTrack.Bind(t) if err != nil { @@ -278,6 +291,7 @@ func (s *TrackLocalStaticSample) Bind(t TrackLocalContext) (RTPCodecParameters, codec.ClockRate, ) s.clockRate = float64(codec.RTPCodecCapability.ClockRate) + return codec, nil } @@ -290,14 +304,14 @@ func (s *TrackLocalStaticSample) Unbind(t TrackLocalContext) error { // WriteSample writes a Sample to the TrackLocalStaticSample // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed -// PeerConnections so you can remove them +// PeerConnections so you can remove them. func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error { s.rtpTrack.mu.RLock() - p := s.packetizer + packetizer := s.packetizer clockRate := s.clockRate s.rtpTrack.mu.RUnlock() - if p == nil { + if packetizer == nil { return nil } @@ -308,9 +322,9 @@ func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error { samples := uint32(sample.Duration.Seconds() * clockRate) if sample.PrevDroppedPackets > 0 { - p.SkipSamples(samples * uint32(sample.PrevDroppedPackets)) + packetizer.SkipSamples(samples * uint32(sample.PrevDroppedPackets)) } - packets := p.Packetize(sample.Data, samples) + packets := packetizer.Packetize(sample.Data, samples) writeErrs := []error{} for _, p := range packets { @@ -325,7 +339,7 @@ func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error { // GeneratePadding writes padding-only samples to the TrackLocalStaticSample // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed -// PeerConnections so you can remove them +// PeerConnections so you can remove them. func (s *TrackLocalStaticSample) GeneratePadding(samples uint32) error { s.rtpTrack.mu.RLock() p := s.packetizer diff --git a/track_local_static_test.go b/track_local_static_test.go index db4e1855..f9494d68 100644 --- a/track_local_static_test.go +++ b/track_local_static_test.go @@ -20,7 +20,7 @@ import ( ) // If a remote doesn't support a Codec used by a `TrackLocalStatic` -// an error should be returned to the user +// an error should be returned to the user. func Test_TrackLocalStatic_NoCodecIntersection(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -50,13 +50,15 @@ func Test_TrackLocalStatic_NoCodecIntersection(t *testing.T) { pc, err := NewPeerConnection(Configuration{}) assert.NoError(t, err) - m := &MediaEngine{} - assert.NoError(t, m.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP9", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + mediaEngine := &MediaEngine{} + assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP9", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo)) - vp9OnlyPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{}) + vp9OnlyPC, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) assert.NoError(t, err) _, err = vp9OnlyPC.AddTransceiverFromKind(RTPCodecTypeVideo) @@ -74,7 +76,9 @@ func Test_TrackLocalStatic_NoCodecIntersection(t *testing.T) { offerer, answerer, err := newPair() assert.NoError(t, err) - invalidCodecTrack, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: "video/invalid-codec"}, "video", "pion") + invalidCodecTrack, err := NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: "video/invalid-codec"}, "video", "pion", + ) assert.NoError(t, err) _, err = offerer.AddTrack(invalidCodecTrack) @@ -85,7 +89,7 @@ func Test_TrackLocalStatic_NoCodecIntersection(t *testing.T) { }) } -// Assert that Bind/Unbind happens when expected +// Assert that Bind/Unbind happens when expected. func Test_TrackLocalStatic_Closed(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -125,14 +129,26 @@ func Test_TrackLocalStatic_PayloadType(t *testing.T) { mediaEngineOne := &MediaEngine{} assert.NoError(t, mediaEngineOne.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 100, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 100, }, RTPCodecTypeVideo)) mediaEngineTwo := &MediaEngine{} assert.NoError(t, mediaEngineTwo.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 200, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 200, }, RTPCodecTypeVideo)) offerer, err := NewAPI(WithMediaEngine(mediaEngineOne)).NewPeerConnection(Configuration{}) @@ -160,13 +176,13 @@ func Test_TrackLocalStatic_PayloadType(t *testing.T) { assert.NoError(t, signalPair(offerer, answerer)) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{track}) closePairNow(t, offerer, answerer) } // Assert that writing to a Track doesn't modify the input -// Even though we can pass a pointer we shouldn't modify the incoming value +// Even though we can pass a pointer we shouldn't modify the incoming value. func Test_TrackLocalStatic_Mutate_Input(t *testing.T) { lim := test.TimeOut(time.Second * 30) defer lim.Stop() @@ -195,7 +211,7 @@ func Test_TrackLocalStatic_Mutate_Input(t *testing.T) { } // Assert that writing to a Track that has Binded (but not connected) -// does not block +// does not block. func Test_TrackLocalStatic_Binding_NonBlocking(t *testing.T) { lim := test.TimeOut(time.Second * 5) defer lim.Stop() @@ -258,14 +274,26 @@ func BenchmarkTrackLocalWrite(b *testing.B) { func Test_TrackLocalStatic_Padding(t *testing.T) { mediaEngineOne := &MediaEngine{} assert.NoError(t, mediaEngineOne.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 100, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 100, }, RTPCodecTypeVideo)) mediaEngineTwo := &MediaEngine{} assert.NoError(t, mediaEngineTwo.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 200, + RTPCodecCapability: RTPCodecCapability{ + MimeType: "video/VP8", + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 200, }, RTPCodecTypeVideo)) offerer, err := NewAPI(WithMediaEngine(mediaEngineOne)).NewPeerConnection(Configuration{}) @@ -345,6 +373,7 @@ type customCodecPayloader struct { func (c *customCodecPayloader) Payload(_ uint16, payload []byte) [][]byte { c.invokeCount.Add(1) + return [][]byte{payload} } @@ -353,8 +382,14 @@ func Test_TrackLocalStatic_Payloader(t *testing.T) { mediaEngine := &MediaEngine{} assert.NoError(t, mediaEngine.RegisterCodec(RTPCodecParameters{ - RTPCodecCapability: RTPCodecCapability{MimeType: mimeTypeCustomCodec, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - PayloadType: 96, + RTPCodecCapability: RTPCodecCapability{ + MimeType: mimeTypeCustomCodec, + ClockRate: 90000, + Channels: 0, + SDPFmtpLine: "", + RTCPFeedback: nil, + }, + PayloadType: 96, }, RTPCodecTypeVideo)) offerer, err := NewAPI(WithMediaEngine(mediaEngine)).NewPeerConnection(Configuration{}) @@ -364,10 +399,16 @@ func Test_TrackLocalStatic_Payloader(t *testing.T) { assert.NoError(t, err) customPayloader := &customCodecPayloader{} - track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: mimeTypeCustomCodec}, "video", "pion", WithPayloader(func(c RTPCodecCapability) (rtp.Payloader, error) { - require.Equal(t, c.MimeType, mimeTypeCustomCodec) - return customPayloader, nil - })) + track, err := NewTrackLocalStaticSample( + RTPCodecCapability{MimeType: mimeTypeCustomCodec}, + "video", + "pion", + WithPayloader(func(c RTPCodecCapability) (rtp.Payloader, error) { + require.Equal(t, c.MimeType, mimeTypeCustomCodec) + + return customPayloader, nil + }), + ) assert.NoError(t, err) _, err = offerer.AddTrack(track) @@ -380,7 +421,7 @@ func Test_TrackLocalStatic_Payloader(t *testing.T) { onTrackFiredFunc() }) - sendVideoUntilDone(onTrackFired.Done(), t, []*TrackLocalStaticSample{track}) + sendVideoUntilDone(t, onTrackFired.Done(), []*TrackLocalStaticSample{track}) closePairNow(t, offerer, answerer) } diff --git a/track_remote.go b/track_remote.go index 7e448dd9..1b037e71 100644 --- a/track_remote.go +++ b/track_remote.go @@ -14,7 +14,7 @@ import ( "github.com/pion/rtp" ) -// TrackRemote represents a single inbound source of media +// TrackRemote represents a single inbound source of media. type TrackRemote struct { mu sync.RWMutex @@ -46,16 +46,17 @@ func newTrackRemote(kind RTPCodecType, ssrc, rtxSsrc SSRC, rid string, receiver // ID is the unique identifier for this Track. This should be unique for the // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video' -// and StreamID would be 'desktop' or 'webcam' +// and StreamID would be 'desktop' or 'webcam'. func (t *TrackRemote) ID() string { t.mu.RLock() defer t.mu.RUnlock() + return t.id } // RID gets the RTP Stream ID of this Track // With Simulcast you will have multiple tracks with the same ID, but different RID values. -// In many cases a TrackRemote will not have an RID, so it is important to assert it is non-zero +// In many cases a TrackRemote will not have an RID, so it is important to assert it is non-zero. func (t *TrackRemote) RID() string { t.mu.RLock() defer t.mu.RUnlock() @@ -63,50 +64,55 @@ func (t *TrackRemote) RID() string { return t.rid } -// PayloadType gets the PayloadType of the track +// PayloadType gets the PayloadType of the track. func (t *TrackRemote) PayloadType() PayloadType { t.mu.RLock() defer t.mu.RUnlock() + return t.payloadType } -// Kind gets the Kind of the track +// Kind gets the Kind of the track. func (t *TrackRemote) Kind() RTPCodecType { t.mu.RLock() defer t.mu.RUnlock() + return t.kind } -// StreamID is the group this track belongs too. This must be unique +// StreamID is the group this track belongs too. This must be unique. func (t *TrackRemote) StreamID() string { t.mu.RLock() defer t.mu.RUnlock() + return t.streamID } -// SSRC gets the SSRC of the track +// SSRC gets the SSRC of the track. func (t *TrackRemote) SSRC() SSRC { t.mu.RLock() defer t.mu.RUnlock() + return t.ssrc } -// Msid gets the Msid of the track +// Msid gets the Msid of the track. func (t *TrackRemote) Msid() string { return t.StreamID() + " " + t.ID() } -// Codec gets the Codec of the track +// Codec gets the Codec of the track. func (t *TrackRemote) Codec() RTPCodecParameters { t.mu.RLock() defer t.mu.RUnlock() + return t.codec } // Read reads data from the track. func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, err error) { t.mu.RLock() - r := t.receiver + receiver := t.receiver peeked := t.peeked != nil t.mu.RUnlock() @@ -123,12 +129,13 @@ func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, if data != nil { n = copy(b, data) err = t.checkAndUpdateTrack(b) - return + + return n, attributes, err } } // If there's a separate RTX track and an RTX packet is available, return that - if rtxPacketReceived := r.readRTX(t); rtxPacketReceived != nil { + if rtxPacketReceived := receiver.readRTX(t); rtxPacketReceived != nil { n = copy(b, rtxPacketReceived.pkt) attributes = rtxPacketReceived.attributes rtxPacketReceived.release() @@ -136,10 +143,11 @@ func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, } else { // If there's no separate RTX track (or there's a separate RTX track but no RTX packet waiting), wait for and return // a packet from the main track - n, attributes, err = r.readRTP(b, t) + n, attributes, err = receiver.readRTP(b, t) if err != nil { - return + return n, attributes, err } + err = t.checkAndUpdateTrack(b) } @@ -147,7 +155,7 @@ func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, } // checkAndUpdateTrack checks payloadType for every incoming packet -// once a different payloadType is detected the track will be updated +// once a different payloadType is detected the track will be updated. func (t *TrackRemote) checkAndUpdateTrack(b []byte) error { if len(b) < 2 { return errRTPTooShort @@ -184,10 +192,11 @@ func (t *TrackRemote) ReadRTP() (*rtp.Packet, interceptor.Attributes, error) { if err := r.Unmarshal(b[:i]); err != nil { return nil, nil, err } + return r, attributes, nil } -// peek is like Read, but it doesn't discard the packet read +// peek is like Read, but it doesn't discard the packet read. func (t *TrackRemote) peek(b []byte) (n int, a interceptor.Attributes, err error) { n, a, err = t.Read(b) if err != nil { @@ -203,6 +212,7 @@ func (t *TrackRemote) peek(b []byte) (n int, a interceptor.Attributes, err error t.peeked = data t.peekedAttributes = a t.mu.Unlock() + return } @@ -211,17 +221,19 @@ func (t *TrackRemote) SetReadDeadline(deadline time.Time) error { return t.receiver.setRTPReadDeadline(deadline, t) } -// RtxSSRC returns the RTX SSRC for a track, or 0 if track does not have a separate RTX stream +// RtxSSRC returns the RTX SSRC for a track, or 0 if track does not have a separate RTX stream. func (t *TrackRemote) RtxSSRC() SSRC { t.mu.RLock() defer t.mu.RUnlock() + return t.rtxSsrc } -// HasRTX returns true if the track has a separate RTX stream +// HasRTX returns true if the track has a separate RTX stream. func (t *TrackRemote) HasRTX() bool { t.mu.RLock() defer t.mu.RUnlock() + return t.rtxSsrc != 0 } diff --git a/vnet_test.go b/vnet_test.go index a527ae66..a9829c49 100644 --- a/vnet_test.go +++ b/vnet_test.go @@ -16,7 +16,12 @@ import ( "github.com/stretchr/testify/assert" ) -func createVNetPair(t *testing.T, interceptorRegistry *interceptor.Registry) (*PeerConnection, *PeerConnection, *vnet.Router) { +func createVNetPair(t *testing.T, interceptorRegistry *interceptor.Registry) ( + *PeerConnection, + *PeerConnection, + *vnet.Router, +) { + t.Helper() // Create a root router wan, err := vnet.NewRouter(&vnet.RouterConfig{ CIDR: "1.2.3.0/24",