feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

24
vendor/github.com/pion/interceptor/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,24 @@
### JetBrains IDE ###
#####################
.idea/
### Emacs Temporary Files ###
#############################
*~
### Folders ###
###############
bin/
vendor/
node_modules/
### Files ###
#############
*.ivf
*.ogg
tags
cover.out
*.sw[poe]
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem

89
vendor/github.com/pion/interceptor/.golangci.yml generated vendored Normal file
View File

@@ -0,0 +1,89 @@
linters-settings:
govet:
check-shadowing: true
misspell:
locale: US
exhaustive:
default-signifies-exhaustive: true
gomodguard:
blocked:
modules:
- github.com/pkg/errors:
recommendations:
- errors
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- bodyclose # checks whether HTTP response body is closed successfully
- deadcode # Finds unused code
- depguard # Go linter that checks if package imports are in a list of acceptable packages
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- exhaustive # check exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
- gci # Gci control golang package import order and make it always deterministic.
- gochecknoglobals # Checks that no globals are present in Go code
- gochecknoinits # Checks that no init functions are present in Go code
- gocognit # Computes and checks the cognitive complexity of functions
- goconst # Finds repeated strings that could be replaced by a constant
- gocritic # The most opinionated Go source code linter
- godox # Tool for detection of FIXME, TODO and other comment keywords
- goerr113 # Golang linter to check the errors handling expressions
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
- goheader # Checks is file header matches to pattern
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
- golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
- gosec # Inspects source code for security problems
- gosimple # Linter for Go source code that specializes in simplifying a code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- ineffassign # Detects when assignments to existing variables are not used
- misspell # Finds commonly misspelled English words in comments
- nakedret # Finds naked returns in functions greater than a specified function length
- noctx # noctx finds sending http request without context.Context
- scopelint # Scopelint checks for unpinned variables in go programs
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- structcheck # Finds unused struct fields
- stylecheck # Stylecheck is a replacement for golint
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
- varcheck # Finds unused global variables and constants
- whitespace # Tool for detection of leading and trailing whitespace
disable:
- funlen # Tool for detection of long functions
- gocyclo # Computes and checks the cyclomatic complexity of functions
- godot # Check if comments end in a period
- gomnd # An analyzer to detect magic numbers.
- lll # Reports long lines
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
- nestif # Reports deeply nested if statements
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- nolintlint # Reports ill-formed or insufficient nolint directives
- prealloc # Finds slice declarations that could potentially be preallocated
- 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
- wsl # Whitespace Linter - Forces you to use empty lines!
issues:
exclude-use-default: false
exclude-rules:
# Allow complex tests, better to be self contained
- path: _test\.go
linters:
- gocognit
# Allow complex main function in examples
- path: examples
text: "of func `main` is high"
linters:
- gocognit
run:
skip-dirs-use-default: false

16
vendor/github.com/pion/interceptor/AUTHORS.txt generated vendored Normal file
View File

@@ -0,0 +1,16 @@
# Thank you to everyone that made Pion possible. If you are interested in contributing
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
#
# This file is auto generated, using git to list all individuals contributors.
# see `.github/generate-authors.sh` for the scripting
Adam Kiss <masterada@gmail.com>
adamroach <adam@nostrum.com>
aler9 <46489434+aler9@users.noreply.github.com>
Antoine Baché <antoine@tenten.app>
Atsushi Watanabe <atsushi.w@ieee.org>
boks1971 <raja.gobi@tutanota.com>
David Zhao <david@davidzhao.com>
Jonathan Müller <jonathan@fotokite.com>
Kevin Caffrey <kcaffrey@gmail.com>
Mathis Engelbart <mathis.engelbart@gmail.com>
Sean DuBois <sean@siobud.com>

21
vendor/github.com/pion/interceptor/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

78
vendor/github.com/pion/interceptor/README.md generated vendored Normal file
View File

@@ -0,0 +1,78 @@
<h1 align="center">
<br>
Pion Interceptor
<br>
</h1>
<h4 align="center">RTCP and RTCP processors for building real time communications</h4>
<p align="center">
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-interceptor-gray.svg?longCache=true&colorB=brightgreen" alt="Pion Interceptor"></a>
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
<br>
<a href="https://pkg.go.dev/github.com/pion/interceptor"><img src="https://godoc.org/github.com/pion/interceptor?status.svg" alt="GoDoc"></a>
<a href="https://codecov.io/gh/pion/interceptor"><img src="https://codecov.io/gh/pion/interceptor/branch/master/graph/badge.svg" alt="Coverage Status"></a>
<a href="https://goreportcard.com/report/github.com/pion/interceptor"><img src="https://goreportcard.com/badge/github.com/pion/interceptor" alt="Go Report Card"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
<br>
Interceptor is a framework for building RTP/RTCP communication software. This framework defines
a interface that each interceptor must satisfy. These interceptors are then run sequentially. We
also then provide common interceptors that will be useful for building RTC software.
This package was built for [pion/webrtc](https://github.com/pion/webrtc), but we designed it to be consumable
by anyone. With the following tenets in mind.
* Useful defaults. Each interceptor will be configured to give you a good default experience.
* Unblock unique use cases. New use cases are what is driving WebRTC, we want to empower them.
* Encourage modification. Add your own interceptors without forking. Mixing with the ones we provide.
* Empower learning. This code base should be useful to read and learn even if you aren't using Pion.
#### Current Interceptors
* [NACK Generator/Responder](https://github.com/pion/interceptor/tree/master/pkg/nack)
* [Sender and Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report)
* [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc)
* [Packet Dump](https://github.com/pion/interceptor/tree/master/pkg/packetdump)
#### Planned Interceptors
* Bandwidth Estimation
- [NADA](https://tools.ietf.org/html/rfc8698)
- [Google Congestion Control](https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02)
* JitterBuffer, re-order packets and wait for arrival
* [FlexFec](https://tools.ietf.org/html/draft-ietf-payload-flexible-fec-scheme-20)
* [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation
* [RTCP Feedback for Congestion Control](https://datatracker.ietf.org/doc/html/rfc8888) the standardized alternative to TWCC.
### Interceptor Public API
The public interface is defined in [interceptor.go](https://github.com/pion/interceptor/blob/master/interceptor.go).
The methods you need to satisy are broken up into 4 groups.
* `BindRTCPWriter` and `BindRTCPReader` allow you to inspect/modify RTCP traffic.
* `BindLocalStream` and `BindRemoteStream` notify you of a new SSRC stream and allow you to inspect/modify.
* `UnbindLocalStream` and `UnbindRemoteStream` notify you when a SSRC stream has been removed
* `Close` called when the interceptor is closed.
Interceptors also pass Attributes between each other. These are a collection of key/value pairs and are useful for storing metadata
or caching.
[noop.go](https://github.com/pion/interceptor/blob/master/noop.go) is an interceptor that satisfies this interface, but does nothing.
You can embed this interceptor as a starting point so you only need to define exactly what you need.
[chain.go]( https://github.com/pion/interceptor/blob/master/chain.go) is used to combine multiple interceptors into one. They are called
sequentially as the packet moves through them.
### Examples
The [examples](https://github.com/pion/interceptor/blob/master/examples) directory provides some basic examples. If you need more please file an issue!
You should also look in [pion/webrtc](https://github.com/pion/webrtc) for real world examples.
### Community
Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
We are always looking to support **your projects**. Please reach out if you have something to build!
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
### License
MIT License - see [LICENSE](LICENSE) for full text

65
vendor/github.com/pion/interceptor/attributes.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package interceptor
import (
"errors"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
type unmarshaledDataKeyType int
const (
rtpHeaderKey unmarshaledDataKeyType = iota
rtcpPacketsKey
)
var errInvalidType = errors.New("found value of invalid type in attributes map")
// Attributes are a generic key/value store used by interceptors
type Attributes map[interface{}]interface{}
// Get returns the attribute associated with key.
func (a Attributes) Get(key interface{}) interface{} {
return a[key]
}
// Set sets the attribute associated with key to the given value.
func (a Attributes) Set(key interface{}, val interface{}) {
a[key] = val
}
// GetRTPHeader gets the RTP header if present. If it is not present, it will be
// unmarshalled from the raw byte slice and stored in the attribtues.
func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) {
if val, ok := a[rtpHeaderKey]; ok {
if header, ok := val.(*rtp.Header); ok {
return header, nil
}
return nil, errInvalidType
}
header := &rtp.Header{}
if _, err := header.Unmarshal(raw); err != nil {
return nil, err
}
a[rtpHeaderKey] = header
return header, nil
}
// GetRTCPPackets gets the RTCP packets if present. If the packet slice is not
// present, it will be unmarshaled from the raw byte slice and stored in the
// attributes.
func (a Attributes) GetRTCPPackets(raw []byte) ([]rtcp.Packet, error) {
if val, ok := a[rtcpPacketsKey]; ok {
if packets, ok := val.([]rtcp.Packet); ok {
return packets, nil
}
return nil, errInvalidType
}
pkts, err := rtcp.Unmarshal(raw)
if err != nil {
return nil, err
}
a[rtcpPacketsKey] = pkts
return pkts, nil
}

75
vendor/github.com/pion/interceptor/chain.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
package interceptor
// Chain is an interceptor that runs all child interceptors in order.
type Chain struct {
interceptors []Interceptor
}
// NewChain returns a new Chain interceptor.
func NewChain(interceptors []Interceptor) *Chain {
return &Chain{interceptors: interceptors}
}
// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (i *Chain) BindRTCPReader(reader RTCPReader) RTCPReader {
for _, interceptor := range i.interceptors {
reader = interceptor.BindRTCPReader(reader)
}
return reader
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (i *Chain) BindRTCPWriter(writer RTCPWriter) RTCPWriter {
for _, interceptor := range i.interceptors {
writer = interceptor.BindRTCPWriter(writer)
}
return writer
}
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (i *Chain) BindLocalStream(ctx *StreamInfo, writer RTPWriter) RTPWriter {
for _, interceptor := range i.interceptors {
writer = interceptor.BindLocalStream(ctx, writer)
}
return writer
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (i *Chain) UnbindLocalStream(ctx *StreamInfo) {
for _, interceptor := range i.interceptors {
interceptor.UnbindLocalStream(ctx)
}
}
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (i *Chain) BindRemoteStream(ctx *StreamInfo, reader RTPReader) RTPReader {
for _, interceptor := range i.interceptors {
reader = interceptor.BindRemoteStream(ctx, reader)
}
return reader
}
// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (i *Chain) UnbindRemoteStream(ctx *StreamInfo) {
for _, interceptor := range i.interceptors {
interceptor.UnbindRemoteStream(ctx)
}
}
// Close closes the Interceptor, cleaning up any data if necessary.
func (i *Chain) Close() error {
var errs []error
for _, interceptor := range i.interceptors {
errs = append(errs, interceptor.Close())
}
return flattenErrs(errs)
}

20
vendor/github.com/pion/interceptor/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,20 @@
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
coverage:
status:
project:
default:
# Allow decreasing 2% of total coverage to avoid noise.
threshold: 2%
patch:
default:
target: 70%
only_pulls: true
ignore:
- "examples/*"
- "examples/**/*"

51
vendor/github.com/pion/interceptor/errors.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
package interceptor
import (
"errors"
"strings"
)
func flattenErrs(errs []error) error {
errs2 := []error{}
for _, e := range errs {
if e != nil {
errs2 = append(errs2, e)
}
}
if len(errs2) == 0 {
return nil
}
return multiError(errs2)
}
type multiError []error
func (me multiError) Error() string {
var errstrings []string
for _, err := range me {
if err != nil {
errstrings = append(errstrings, err.Error())
}
}
if len(errstrings) == 0 {
return "multiError must contain multiple error but is empty"
}
return strings.Join(errstrings, "\n")
}
func (me multiError) Is(err error) bool {
for _, e := range me {
if errors.Is(e, err) {
return true
}
if me2, ok := e.(multiError); ok {
if me2.Is(err) {
return true
}
}
}
return false
}

100
vendor/github.com/pion/interceptor/interceptor.go generated vendored Normal file
View File

@@ -0,0 +1,100 @@
// Package interceptor contains the Interceptor interface, with some useful interceptors that should be safe to use
// in most cases.
package interceptor
import (
"io"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
// Factory provides an interface for constructing interceptors
type Factory interface {
NewInterceptor(id string) (Interceptor, error)
}
// Interceptor can be used to add functionality to you PeerConnections by modifying any incoming/outgoing rtp/rtcp
// packets, or sending your own packets as needed.
type Interceptor interface {
// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
BindRTCPReader(reader RTCPReader) RTCPReader
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
BindRTCPWriter(writer RTCPWriter) RTCPWriter
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
BindLocalStream(info *StreamInfo, writer RTPWriter) RTPWriter
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
UnbindLocalStream(info *StreamInfo)
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
BindRemoteStream(info *StreamInfo, reader RTPReader) RTPReader
// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
UnbindRemoteStream(info *StreamInfo)
io.Closer
}
// RTPWriter is used by Interceptor.BindLocalStream.
type RTPWriter interface {
// Write a rtp packet
Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error)
}
// RTPReader is used by Interceptor.BindRemoteStream.
type RTPReader interface {
// Read a rtp packet
Read([]byte, Attributes) (int, Attributes, error)
}
// RTCPWriter is used by Interceptor.BindRTCPWriter.
type RTCPWriter interface {
// Write a batch of rtcp packets
Write(pkts []rtcp.Packet, attributes Attributes) (int, error)
}
// RTCPReader is used by Interceptor.BindRTCPReader.
type RTCPReader interface {
// Read a batch of rtcp packets
Read([]byte, Attributes) (int, Attributes, error)
}
// RTPWriterFunc is an adapter for RTPWrite interface
type RTPWriterFunc func(header *rtp.Header, payload []byte, attributes Attributes) (int, error)
// RTPReaderFunc is an adapter for RTPReader interface
type RTPReaderFunc func([]byte, Attributes) (int, Attributes, error)
// RTCPWriterFunc is an adapter for RTCPWriter interface
type RTCPWriterFunc func(pkts []rtcp.Packet, attributes Attributes) (int, error)
// RTCPReaderFunc is an adapter for RTCPReader interface
type RTCPReaderFunc func([]byte, Attributes) (int, Attributes, error)
// Write a rtp packet
func (f RTPWriterFunc) Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error) {
return f(header, payload, attributes)
}
// Read a rtp packet
func (f RTPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
return f(b, a)
}
// Write a batch of rtcp packets
func (f RTCPWriterFunc) Write(pkts []rtcp.Packet, attributes Attributes) (int, error) {
return f(pkts, attributes)
}
// Read a batch of rtcp packets
func (f RTCPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
return f(b, a)
}

40
vendor/github.com/pion/interceptor/noop.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package interceptor
// NoOp is an Interceptor that does not modify any packets. It can embedded in other interceptors, so it's
// possible to implement only a subset of the methods.
type NoOp struct{}
// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (i *NoOp) BindRTCPReader(reader RTCPReader) RTCPReader {
return reader
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (i *NoOp) BindRTCPWriter(writer RTCPWriter) RTCPWriter {
return writer
}
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (i *NoOp) BindLocalStream(_ *StreamInfo, writer RTPWriter) RTPWriter {
return writer
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (i *NoOp) UnbindLocalStream(_ *StreamInfo) {}
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (i *NoOp) BindRemoteStream(_ *StreamInfo, reader RTPReader) RTPReader {
return reader
}
// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (i *NoOp) UnbindRemoteStream(_ *StreamInfo) {}
// Close closes the Interceptor, cleaning up any data if necessary.
func (i *NoOp) Close() error {
return nil
}

View File

@@ -0,0 +1,8 @@
package nack
import "errors"
// ErrInvalidSize is returned by newReceiveLog/newSendBuffer, when an incorrect buffer size is supplied.
var ErrInvalidSize = errors.New("invalid buffer size")
var errPacketReleased = errors.New("could not retain packet, already released")

View File

@@ -0,0 +1,175 @@
package nack
import (
"math/rand"
"sync"
"time"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtcp"
)
// GeneratorInterceptorFactory is a interceptor.Factory for a GeneratorInterceptor
type GeneratorInterceptorFactory struct {
opts []GeneratorOption
}
// NewInterceptor constructs a new ReceiverInterceptor
func (g *GeneratorInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &GeneratorInterceptor{
size: 512,
skipLastN: 0,
interval: time.Millisecond * 100,
receiveLogs: map[uint32]*receiveLog{},
close: make(chan struct{}),
log: logging.NewDefaultLoggerFactory().NewLogger("nack_generator"),
}
for _, opt := range g.opts {
if err := opt(i); err != nil {
return nil, err
}
}
if _, err := newReceiveLog(i.size); err != nil {
return nil, err
}
return i, nil
}
// GeneratorInterceptor interceptor generates nack feedback messages.
type GeneratorInterceptor struct {
interceptor.NoOp
size uint16
skipLastN uint16
interval time.Duration
m sync.Mutex
wg sync.WaitGroup
close chan struct{}
log logging.LeveledLogger
receiveLogs map[uint32]*receiveLog
receiveLogsMu sync.Mutex
}
// NewGeneratorInterceptor returns a new GeneratorInterceptorFactory
func NewGeneratorInterceptor(opts ...GeneratorOption) (*GeneratorInterceptorFactory, error) {
return &GeneratorInterceptorFactory{opts}, nil
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
n.m.Lock()
defer n.m.Unlock()
if n.isClosed() {
return writer
}
n.wg.Add(1)
go n.loop(writer)
return writer
}
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
if !streamSupportNack(info) {
return reader
}
// error is already checked in NewGeneratorInterceptor
receiveLog, _ := newReceiveLog(n.size)
n.receiveLogsMu.Lock()
n.receiveLogs[info.SSRC] = receiveLog
n.receiveLogsMu.Unlock()
return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(b, a)
if err != nil {
return 0, nil, err
}
if attr == nil {
attr = make(interceptor.Attributes)
}
header, err := attr.GetRTPHeader(b[:i])
if err != nil {
return 0, nil, err
}
receiveLog.add(header.SequenceNumber)
return i, attr, nil
})
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (n *GeneratorInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
n.receiveLogsMu.Lock()
delete(n.receiveLogs, info.SSRC)
n.receiveLogsMu.Unlock()
}
// Close closes the interceptor
func (n *GeneratorInterceptor) Close() error {
defer n.wg.Wait()
n.m.Lock()
defer n.m.Unlock()
if !n.isClosed() {
close(n.close)
}
return nil
}
func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer n.wg.Done()
senderSSRC := rand.Uint32() // #nosec
ticker := time.NewTicker(n.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
func() {
n.receiveLogsMu.Lock()
defer n.receiveLogsMu.Unlock()
for ssrc, receiveLog := range n.receiveLogs {
missing := receiveLog.missingSeqNumbers(n.skipLastN)
if len(missing) == 0 {
continue
}
nack := &rtcp.TransportLayerNack{
SenderSSRC: senderSSRC,
MediaSSRC: ssrc,
Nacks: rtcp.NackPairsFromSequenceNumbers(missing),
}
if _, err := rtcpWriter.Write([]rtcp.Packet{nack}, interceptor.Attributes{}); err != nil {
n.log.Warnf("failed sending nack: %+v", err)
}
}
}()
case <-n.close:
return
}
}
}
func (n *GeneratorInterceptor) isClosed() bool {
select {
case <-n.close:
return true
default:
return false
}
}

View File

@@ -0,0 +1,44 @@
package nack
import (
"time"
"github.com/pion/logging"
)
// GeneratorOption can be used to configure GeneratorInterceptor
type GeneratorOption func(r *GeneratorInterceptor) error
// GeneratorSize sets the size of the interceptor.
// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
func GeneratorSize(size uint16) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.size = size
return nil
}
}
// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets) to ignore when generating
// nack requests.
func GeneratorSkipLastN(skipLastN uint16) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.skipLastN = skipLastN
return nil
}
}
// GeneratorLog sets a logger for the interceptor
func GeneratorLog(log logging.LeveledLogger) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.log = log
return nil
}
}
// GeneratorInterval sets the nack send interval for the interceptor
func GeneratorInterval(interval time.Duration) GeneratorOption {
return func(r *GeneratorInterceptor) error {
r.interval = interval
return nil
}
}

14
vendor/github.com/pion/interceptor/pkg/nack/nack.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Package nack provides interceptors to implement sending and receiving negative acknowledgements
package nack
import "github.com/pion/interceptor"
func streamSupportNack(info *interceptor.StreamInfo) bool {
for _, fb := range info.RTCPFeedback {
if fb.Type == "nack" && fb.Parameter == "" {
return true
}
}
return false
}

View File

@@ -0,0 +1,134 @@
package nack
import (
"fmt"
"sync"
)
type receiveLog struct {
packets []uint64
size uint16
end uint16
started bool
lastConsecutive uint16
m sync.RWMutex
}
func newReceiveLog(size uint16) (*receiveLog, error) {
allowedSizes := make([]uint16, 0)
correctSize := false
for i := 6; i < 16; i++ {
if size == 1<<i {
correctSize = true
break
}
allowedSizes = append(allowedSizes, 1<<i)
}
if !correctSize {
return nil, fmt.Errorf("%w: %d is not a valid size, allowed sizes: %v", ErrInvalidSize, size, allowedSizes)
}
return &receiveLog{
packets: make([]uint64, size/64),
size: size,
}, nil
}
func (s *receiveLog) add(seq uint16) {
s.m.Lock()
defer s.m.Unlock()
if !s.started {
s.setReceived(seq)
s.end = seq
s.started = true
s.lastConsecutive = seq
return
}
diff := seq - s.end
switch {
case diff == 0:
return
case diff < uint16SizeHalf:
// this means a positive diff, in other words seq > end (with counting for rollovers)
for i := s.end + 1; i != seq; i++ {
// clear packets between end and seq (these may contain packets from a "size" ago)
s.delReceived(i)
}
s.end = seq
if s.lastConsecutive+1 == seq {
s.lastConsecutive = seq
} else if seq-s.lastConsecutive > s.size {
s.lastConsecutive = seq - s.size
s.fixLastConsecutive() // there might be valid packets at the beginning of the buffer now
}
case s.lastConsecutive+1 == seq:
// negative diff, seq < end (with counting for rollovers)
s.lastConsecutive = seq
s.fixLastConsecutive() // there might be other valid packets after seq
}
s.setReceived(seq)
}
func (s *receiveLog) get(seq uint16) bool {
s.m.RLock()
defer s.m.RUnlock()
diff := s.end - seq
if diff >= uint16SizeHalf {
return false
}
if diff >= s.size {
return false
}
return s.getReceived(seq)
}
func (s *receiveLog) missingSeqNumbers(skipLastN uint16) []uint16 {
s.m.RLock()
defer s.m.RUnlock()
until := s.end - skipLastN
if until-s.lastConsecutive >= uint16SizeHalf {
// until < s.lastConsecutive (counting for rollover)
return nil
}
missingPacketSeqNums := make([]uint16, 0)
for i := s.lastConsecutive + 1; i != until+1; i++ {
if !s.getReceived(i) {
missingPacketSeqNums = append(missingPacketSeqNums, i)
}
}
return missingPacketSeqNums
}
func (s *receiveLog) setReceived(seq uint16) {
pos := seq % s.size
s.packets[pos/64] |= 1 << (pos % 64)
}
func (s *receiveLog) delReceived(seq uint16) {
pos := seq % s.size
s.packets[pos/64] &^= 1 << (pos % 64)
}
func (s *receiveLog) getReceived(seq uint16) bool {
pos := seq % s.size
return (s.packets[pos/64] & (1 << (pos % 64))) != 0
}
func (s *receiveLog) fixLastConsecutive() {
i := s.lastConsecutive + 1
for ; i != s.end+1 && s.getReceived(i); i++ {
// find all consecutive packets
}
s.lastConsecutive = i - 1
}

View File

@@ -0,0 +1,139 @@
package nack
import (
"sync"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
// ResponderInterceptorFactory is a interceptor.Factory for a ResponderInterceptor
type ResponderInterceptorFactory struct {
opts []ResponderOption
}
// NewInterceptor constructs a new ResponderInterceptor
func (r *ResponderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &ResponderInterceptor{
size: 8192,
log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
streams: map[uint32]*localStream{},
packetMan: newPacketManager(),
}
for _, opt := range r.opts {
if err := opt(i); err != nil {
return nil, err
}
}
if _, err := newSendBuffer(i.size); err != nil {
return nil, err
}
return i, nil
}
// ResponderInterceptor responds to nack feedback messages
type ResponderInterceptor struct {
interceptor.NoOp
size uint16
log logging.LeveledLogger
packetMan *packetManager
streams map[uint32]*localStream
streamsMu sync.Mutex
}
type localStream struct {
sendBuffer *sendBuffer
rtpWriter interceptor.RTPWriter
}
// NewResponderInterceptor returns a new ResponderInterceptorFactor
func NewResponderInterceptor(opts ...ResponderOption) (*ResponderInterceptorFactory, error) {
return &ResponderInterceptorFactory{opts}, nil
}
// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (n *ResponderInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
return interceptor.RTCPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(b, a)
if err != nil {
return 0, nil, err
}
if attr == nil {
attr = make(interceptor.Attributes)
}
pkts, err := attr.GetRTCPPackets(b[:i])
if err != nil {
return 0, nil, err
}
for _, rtcpPacket := range pkts {
nack, ok := rtcpPacket.(*rtcp.TransportLayerNack)
if !ok {
continue
}
go n.resendPackets(nack)
}
return i, attr, err
})
}
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
if !streamSupportNack(info) {
return writer
}
// error is already checked in NewGeneratorInterceptor
sendBuffer, _ := newSendBuffer(n.size)
n.streamsMu.Lock()
n.streams[info.SSRC] = &localStream{sendBuffer: sendBuffer, rtpWriter: writer}
n.streamsMu.Unlock()
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
pkt, err := n.packetMan.NewPacket(header, payload)
if err != nil {
return 0, err
}
sendBuffer.add(pkt)
return writer.Write(header, payload, attributes)
})
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (n *ResponderInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
n.streamsMu.Lock()
delete(n.streams, info.SSRC)
n.streamsMu.Unlock()
}
func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) {
n.streamsMu.Lock()
stream, ok := n.streams[nack.MediaSSRC]
n.streamsMu.Unlock()
if !ok {
return
}
for i := range nack.Nacks {
nack.Nacks[i].Range(func(seq uint16) bool {
if p := stream.sendBuffer.get(seq); p != nil {
if _, err := stream.rtpWriter.Write(p.Header(), p.Payload(), interceptor.Attributes{}); err != nil {
n.log.Warnf("failed resending nacked packet: %+v", err)
}
p.Release()
}
return true
})
}
}

View File

@@ -0,0 +1,23 @@
package nack
import "github.com/pion/logging"
// ResponderOption can be used to configure ResponderInterceptor
type ResponderOption func(s *ResponderInterceptor) error
// ResponderSize sets the size of the interceptor.
// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
func ResponderSize(size uint16) ResponderOption {
return func(r *ResponderInterceptor) error {
r.size = size
return nil
}
}
// ResponderLog sets a logger for the interceptor
func ResponderLog(log logging.LeveledLogger) ResponderOption {
return func(r *ResponderInterceptor) error {
r.log = log
return nil
}
}

View File

@@ -0,0 +1,105 @@
package nack
import (
"io"
"sync"
"github.com/pion/rtp"
)
const maxPayloadLen = 1460
type packetManager struct {
headerPool *sync.Pool
payloadPool *sync.Pool
}
func newPacketManager() *packetManager {
return &packetManager{
headerPool: &sync.Pool{
New: func() interface{} {
return &rtp.Header{}
},
},
payloadPool: &sync.Pool{
New: func() interface{} {
buf := make([]byte, maxPayloadLen)
return &buf
},
},
}
}
func (m *packetManager) NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error) {
if len(payload) > maxPayloadLen {
return nil, io.ErrShortBuffer
}
p := &retainablePacket{
onRelease: m.releasePacket,
// new packets have retain count of 1
count: 1,
}
p.header = m.headerPool.Get().(*rtp.Header)
*p.header = header.Clone()
if payload != nil {
p.buffer = m.payloadPool.Get().(*[]byte)
size := copy(*p.buffer, payload)
p.payload = (*p.buffer)[:size]
}
return p, nil
}
func (m *packetManager) releasePacket(header *rtp.Header, payload *[]byte) {
m.headerPool.Put(header)
if payload != nil {
m.payloadPool.Put(payload)
}
}
type retainablePacket struct {
onRelease func(*rtp.Header, *[]byte)
countMu sync.Mutex
count int
header *rtp.Header
buffer *[]byte
payload []byte
}
func (p *retainablePacket) Header() *rtp.Header {
return p.header
}
func (p *retainablePacket) Payload() []byte {
return p.payload
}
func (p *retainablePacket) Retain() error {
p.countMu.Lock()
defer p.countMu.Unlock()
if p.count == 0 {
// already released
return errPacketReleased
}
p.count++
return nil
}
func (p *retainablePacket) Release() {
p.countMu.Lock()
defer p.countMu.Unlock()
p.count--
if p.count == 0 {
// release back to pool
p.onRelease(p.header, p.buffer)
p.header = nil
p.buffer = nil
p.payload = nil
}
}

View File

@@ -0,0 +1,101 @@
package nack
import (
"fmt"
"sync"
)
const (
uint16SizeHalf = 1 << 15
)
type sendBuffer struct {
packets []*retainablePacket
size uint16
lastAdded uint16
started bool
m sync.RWMutex
}
func newSendBuffer(size uint16) (*sendBuffer, error) {
allowedSizes := make([]uint16, 0)
correctSize := false
for i := 0; i < 16; i++ {
if size == 1<<i {
correctSize = true
break
}
allowedSizes = append(allowedSizes, 1<<i)
}
if !correctSize {
return nil, fmt.Errorf("%w: %d is not a valid size, allowed sizes: %v", ErrInvalidSize, size, allowedSizes)
}
return &sendBuffer{
packets: make([]*retainablePacket, size),
size: size,
}, nil
}
func (s *sendBuffer) add(packet *retainablePacket) {
s.m.Lock()
defer s.m.Unlock()
seq := packet.Header().SequenceNumber
if !s.started {
s.packets[seq%s.size] = packet
s.lastAdded = seq
s.started = true
return
}
diff := seq - s.lastAdded
if diff == 0 {
return
} else if diff < uint16SizeHalf {
for i := s.lastAdded + 1; i != seq; i++ {
idx := i % s.size
prevPacket := s.packets[idx]
if prevPacket != nil {
prevPacket.Release()
}
s.packets[idx] = nil
}
}
idx := seq % s.size
prevPacket := s.packets[idx]
if prevPacket != nil {
prevPacket.Release()
}
s.packets[idx] = packet
s.lastAdded = seq
}
func (s *sendBuffer) get(seq uint16) *retainablePacket {
s.m.RLock()
defer s.m.RUnlock()
diff := s.lastAdded - seq
if diff >= uint16SizeHalf {
return nil
}
if diff >= s.size {
return nil
}
pkt := s.packets[seq%s.size]
if pkt != nil {
if pkt.Header().SequenceNumber != seq {
return nil
}
// already released
if err := pkt.Retain(); err != nil {
return nil
}
}
return pkt
}

View File

@@ -0,0 +1,182 @@
package report
import (
"sync"
"time"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtcp"
)
// ReceiverInterceptorFactory is a interceptor.Factory for a ReceiverInterceptor
type ReceiverInterceptorFactory struct {
opts []ReceiverOption
}
// NewInterceptor constructs a new ReceiverInterceptor
func (r *ReceiverInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &ReceiverInterceptor{
interval: 1 * time.Second,
now: time.Now,
log: logging.NewDefaultLoggerFactory().NewLogger("receiver_interceptor"),
close: make(chan struct{}),
}
for _, opt := range r.opts {
if err := opt(i); err != nil {
return nil, err
}
}
return i, nil
}
// NewReceiverInterceptor returns a new ReceiverInterceptorFactory
func NewReceiverInterceptor(opts ...ReceiverOption) (*ReceiverInterceptorFactory, error) {
return &ReceiverInterceptorFactory{opts}, nil
}
// ReceiverInterceptor interceptor generates receiver reports.
type ReceiverInterceptor struct {
interceptor.NoOp
interval time.Duration
now func() time.Time
streams sync.Map
log logging.LeveledLogger
m sync.Mutex
wg sync.WaitGroup
close chan struct{}
}
func (r *ReceiverInterceptor) isClosed() bool {
select {
case <-r.close:
return true
default:
return false
}
}
// Close closes the interceptor.
func (r *ReceiverInterceptor) Close() error {
defer r.wg.Wait()
r.m.Lock()
defer r.m.Unlock()
if !r.isClosed() {
close(r.close)
}
return nil
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (r *ReceiverInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
r.m.Lock()
defer r.m.Unlock()
if r.isClosed() {
return writer
}
r.wg.Add(1)
go r.loop(writer)
return writer
}
func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer r.wg.Done()
ticker := time.NewTicker(r.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
now := r.now()
r.streams.Range(func(key, value interface{}) bool {
stream := value.(*receiverStream)
var pkts []rtcp.Packet
pkts = append(pkts, stream.generateReport(now))
if _, err := rtcpWriter.Write(pkts, interceptor.Attributes{}); err != nil {
r.log.Warnf("failed sending: %+v", err)
}
return true
})
case <-r.close:
return
}
}
}
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
stream := newReceiverStream(info.SSRC, info.ClockRate)
r.streams.Store(info.SSRC, stream)
return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(b, a)
if err != nil {
return 0, nil, err
}
if attr == nil {
attr = make(interceptor.Attributes)
}
header, err := attr.GetRTPHeader(b[:i])
if err != nil {
return 0, nil, err
}
stream.processRTP(r.now(), header)
return i, attr, nil
})
}
// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
func (r *ReceiverInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
r.streams.Delete(info.SSRC)
}
// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
// change in the future. The returned method will be called once per packet batch.
func (r *ReceiverInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
return interceptor.RTCPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(b, a)
if err != nil {
return 0, nil, err
}
if attr == nil {
attr = make(interceptor.Attributes)
}
pkts, err := attr.GetRTCPPackets(b[:i])
if err != nil {
return 0, nil, err
}
for _, pkt := range pkts {
if sr, ok := (pkt).(*rtcp.SenderReport); ok {
value, ok := r.streams.Load(sr.SSRC)
if !ok {
continue
}
stream := value.(*receiverStream)
stream.processSenderReport(r.now(), sr)
}
}
return i, attr, nil
})
}

View File

@@ -0,0 +1,34 @@
package report
import (
"time"
"github.com/pion/logging"
)
// ReceiverOption can be used to configure ReceiverInterceptor.
type ReceiverOption func(r *ReceiverInterceptor) error
// ReceiverLog sets a logger for the interceptor.
func ReceiverLog(log logging.LeveledLogger) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.log = log
return nil
}
}
// ReceiverInterval sets send interval for the interceptor.
func ReceiverInterval(interval time.Duration) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.interval = interval
return nil
}
}
// ReceiverNow sets an alternative for the time.Now function.
func ReceiverNow(f func() time.Time) ReceiverOption {
return func(r *ReceiverInterceptor) error {
r.now = f
return nil
}
}

View File

@@ -0,0 +1,159 @@
package report
import (
"math/rand"
"sync"
"time"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
type receiverStream struct {
ssrc uint32
receiverSSRC uint32
clockRate float64
m sync.Mutex
size uint16
packets []uint64
started bool
seqnumCycles uint16
lastSeqnum uint16
lastReportSeqnum uint16
lastRTPTimeRTP uint32
lastRTPTimeTime time.Time
jitter float64
lastSenderReport uint32
lastSenderReportTime time.Time
totalLost uint32
}
func newReceiverStream(ssrc uint32, clockRate uint32) *receiverStream {
receiverSSRC := rand.Uint32() // #nosec
return &receiverStream{
ssrc: ssrc,
receiverSSRC: receiverSSRC,
clockRate: float64(clockRate),
size: 128,
packets: make([]uint64, 128),
}
}
func (stream *receiverStream) processRTP(now time.Time, pktHeader *rtp.Header) {
stream.m.Lock()
defer stream.m.Unlock()
if !stream.started { // first frame
stream.started = true
stream.setReceived(pktHeader.SequenceNumber)
stream.lastSeqnum = pktHeader.SequenceNumber
stream.lastReportSeqnum = pktHeader.SequenceNumber - 1
stream.lastRTPTimeRTP = pktHeader.Timestamp
stream.lastRTPTimeTime = now
} else { // following frames
stream.setReceived(pktHeader.SequenceNumber)
diff := int32(pktHeader.SequenceNumber) - int32(stream.lastSeqnum)
if diff > 0 || diff < -0x0FFF {
// overflow
if diff < -0x0FFF {
stream.seqnumCycles++
}
// set missing packets as missing
for i := stream.lastSeqnum + 1; i != pktHeader.SequenceNumber; i++ {
stream.delReceived(i)
}
stream.lastSeqnum = pktHeader.SequenceNumber
}
// compute jitter
// https://tools.ietf.org/html/rfc3550#page-39
D := now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate -
(float64(pktHeader.Timestamp) - float64(stream.lastRTPTimeRTP))
if D < 0 {
D = -D
}
stream.jitter += (D - stream.jitter) / 16
stream.lastRTPTimeRTP = pktHeader.Timestamp
stream.lastRTPTimeTime = now
}
}
func (stream *receiverStream) setReceived(seq uint16) {
pos := seq % stream.size
stream.packets[pos/64] |= 1 << (pos % 64)
}
func (stream *receiverStream) delReceived(seq uint16) {
pos := seq % stream.size
stream.packets[pos/64] &^= 1 << (pos % 64)
}
func (stream *receiverStream) getReceived(seq uint16) bool {
pos := seq % stream.size
return (stream.packets[pos/64] & (1 << (pos % 64))) != 0
}
func (stream *receiverStream) processSenderReport(now time.Time, sr *rtcp.SenderReport) {
stream.m.Lock()
defer stream.m.Unlock()
stream.lastSenderReport = uint32(sr.NTPTime >> 16)
stream.lastSenderReportTime = now
}
func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport {
stream.m.Lock()
defer stream.m.Unlock()
totalSinceReport := stream.lastSeqnum - stream.lastReportSeqnum
totalLostSinceReport := func() uint32 {
if stream.lastSeqnum == stream.lastReportSeqnum {
return 0
}
ret := uint32(0)
for i := stream.lastReportSeqnum + 1; i != stream.lastSeqnum; i++ {
if !stream.getReceived(i) {
ret++
}
}
return ret
}()
stream.totalLost += totalLostSinceReport
// allow up to 24 bits
if totalLostSinceReport > 0xFFFFFF {
totalLostSinceReport = 0xFFFFFF
}
if stream.totalLost > 0xFFFFFF {
stream.totalLost = 0xFFFFFF
}
r := &rtcp.ReceiverReport{
SSRC: stream.receiverSSRC,
Reports: []rtcp.ReceptionReport{
{
SSRC: stream.ssrc,
LastSequenceNumber: uint32(stream.seqnumCycles)<<16 | uint32(stream.lastSeqnum),
LastSenderReport: stream.lastSenderReport,
FractionLost: uint8(float64(totalLostSinceReport*256) / float64(totalSinceReport)),
TotalLost: stream.totalLost,
Delay: func() uint32 {
if stream.lastSenderReportTime.IsZero() {
return 0
}
return uint32(now.Sub(stream.lastSenderReportTime).Seconds() * 65536)
}(),
Jitter: uint32(stream.jitter),
},
},
}
stream.lastReportSeqnum = stream.lastSeqnum
return r
}

View File

@@ -0,0 +1,2 @@
// Package report provides interceptors to implement sending sender and receiver reports.
package report

View File

@@ -0,0 +1,150 @@
package report
import (
"sync"
"time"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtcp"
"github.com/pion/rtp"
)
// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
type SenderInterceptorFactory struct {
opts []SenderOption
}
// NewInterceptor constructs a new SenderInterceptor
func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &SenderInterceptor{
interval: 1 * time.Second,
now: time.Now,
log: logging.NewDefaultLoggerFactory().NewLogger("sender_interceptor"),
close: make(chan struct{}),
}
for _, opt := range s.opts {
if err := opt(i); err != nil {
return nil, err
}
}
return i, nil
}
// NewSenderInterceptor returns a new SenderInterceptorFactory
func NewSenderInterceptor(opts ...SenderOption) (*SenderInterceptorFactory, error) {
return &SenderInterceptorFactory{opts}, nil
}
// SenderInterceptor interceptor generates sender reports.
type SenderInterceptor struct {
interceptor.NoOp
interval time.Duration
now func() time.Time
streams sync.Map
log logging.LeveledLogger
m sync.Mutex
wg sync.WaitGroup
close chan struct{}
}
func (s *SenderInterceptor) isClosed() bool {
select {
case <-s.close:
return true
default:
return false
}
}
// Close closes the interceptor.
func (s *SenderInterceptor) Close() error {
defer s.wg.Wait()
s.m.Lock()
defer s.m.Unlock()
if !s.isClosed() {
close(s.close)
}
return nil
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
s.m.Lock()
defer s.m.Unlock()
if s.isClosed() {
return writer
}
s.wg.Add(1)
go s.loop(writer)
return writer
}
func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
defer s.wg.Done()
ticker := time.NewTicker(s.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
now := s.now()
s.streams.Range(func(key, value interface{}) bool {
ssrc := key.(uint32)
stream := value.(*senderStream)
stream.m.Lock()
defer stream.m.Unlock()
sr := &rtcp.SenderReport{
SSRC: ssrc,
NTPTime: ntpTime(now),
RTPTime: stream.lastRTPTimeRTP + uint32(now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate),
PacketCount: stream.packetCount,
OctetCount: stream.octetCount,
}
if _, err := rtcpWriter.Write([]rtcp.Packet{sr}, interceptor.Attributes{}); err != nil {
s.log.Warnf("failed sending: %+v", err)
}
return true
})
case <-s.close:
return
}
}
}
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
// will be called once per rtp packet.
func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
stream := newSenderStream(info.ClockRate)
s.streams.Store(info.SSRC, stream)
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, a interceptor.Attributes) (int, error) {
stream.processRTP(s.now(), header, payload)
return writer.Write(header, payload, a)
})
}
func ntpTime(t time.Time) uint64 {
// seconds since 1st January 1900
s := (float64(t.UnixNano()) / 1000000000) + 2208988800
// higher 32 bits are the integer part, lower 32 bits are the fractional part
integerPart := uint32(s)
fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
return uint64(integerPart)<<32 | uint64(fractionalPart)
}

View File

@@ -0,0 +1,34 @@
package report
import (
"time"
"github.com/pion/logging"
)
// SenderOption can be used to configure SenderInterceptor.
type SenderOption func(r *SenderInterceptor) error
// SenderLog sets a logger for the interceptor.
func SenderLog(log logging.LeveledLogger) SenderOption {
return func(r *SenderInterceptor) error {
r.log = log
return nil
}
}
// SenderInterval sets send interval for the interceptor.
func SenderInterval(interval time.Duration) SenderOption {
return func(r *SenderInterceptor) error {
r.interval = interval
return nil
}
}
// SenderNow sets an alternative for the time.Now function.
func SenderNow(f func() time.Time) SenderOption {
return func(r *SenderInterceptor) error {
r.now = f
return nil
}
}

View File

@@ -0,0 +1,37 @@
package report
import (
"sync"
"time"
"github.com/pion/rtp"
)
type senderStream struct {
clockRate float64
m sync.Mutex
// data from rtp packets
lastRTPTimeRTP uint32
lastRTPTimeTime time.Time
packetCount uint32
octetCount uint32
}
func newSenderStream(clockRate uint32) *senderStream {
return &senderStream{
clockRate: float64(clockRate),
}
}
func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payload []byte) {
stream.m.Lock()
defer stream.m.Unlock()
// always update time to minimize errors
stream.lastRTPTimeRTP = header.Timestamp
stream.lastRTPTimeTime = now
stream.packetCount++
stream.octetCount += uint32(len(payload))
}

View File

@@ -0,0 +1,57 @@
package twcc
import (
"sync/atomic"
"github.com/pion/interceptor"
"github.com/pion/rtp"
)
// HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor
type HeaderExtensionInterceptorFactory struct{}
// NewInterceptor constructs a new HeaderExtensionInterceptor
func (h *HeaderExtensionInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
return &HeaderExtensionInterceptor{}, nil
}
// NewHeaderExtensionInterceptor returns a HeaderExtensionInterceptorFactory
func NewHeaderExtensionInterceptor() (*HeaderExtensionInterceptorFactory, error) {
return &HeaderExtensionInterceptorFactory{}, nil
}
// HeaderExtensionInterceptor adds transport wide sequence numbers as header extension to each RTP packet
type HeaderExtensionInterceptor struct {
interceptor.NoOp
nextSequenceNr uint32
}
const transportCCURI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
// BindLocalStream returns a writer that adds a rtp.TransportCCExtension
// header with increasing sequence numbers to each outgoing packet.
func (h *HeaderExtensionInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
var hdrExtID uint8
for _, e := range info.RTPHeaderExtensions {
if e.URI == transportCCURI {
hdrExtID = uint8(e.ID)
break
}
}
if hdrExtID == 0 { // Don't add header extension if ID is 0, because 0 is an invalid extension ID
return writer
}
return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
sequenceNumber := atomic.AddUint32(&h.nextSequenceNr, 1) - 1
tcc, err := (&rtp.TransportCCExtension{TransportSequence: uint16(sequenceNumber)}).Marshal()
if err != nil {
return 0, err
}
err = header.SetExtension(hdrExtID, tcc)
if err != nil {
return 0, err
}
return writer.Write(header, payload, attributes)
})
}

View File

@@ -0,0 +1,189 @@
package twcc
import (
"math/rand"
"sync"
"time"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtp"
)
// SenderInterceptorFactory is a interceptor.Factory for a SenderInterceptor
type SenderInterceptorFactory struct {
opts []Option
}
// NewInterceptor constructs a new SenderInterceptor
func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) {
i := &SenderInterceptor{
log: logging.NewDefaultLoggerFactory().NewLogger("twcc_sender_interceptor"),
packetChan: make(chan packet),
close: make(chan struct{}),
interval: 100 * time.Millisecond,
startTime: time.Now(),
}
for _, opt := range s.opts {
err := opt(i)
if err != nil {
return nil, err
}
}
return i, nil
}
// NewSenderInterceptor returns a new SenderInterceptorFactory configured with the given options.
func NewSenderInterceptor(opts ...Option) (*SenderInterceptorFactory, error) {
return &SenderInterceptorFactory{opts: opts}, nil
}
// SenderInterceptor sends transport wide congestion control reports as specified in:
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
type SenderInterceptor struct {
interceptor.NoOp
log logging.LeveledLogger
m sync.Mutex
wg sync.WaitGroup
close chan struct{}
interval time.Duration
startTime time.Time
recorder *Recorder
packetChan chan packet
}
// An Option is a function that can be used to configure a SenderInterceptor
type Option func(*SenderInterceptor) error
// SendInterval sets the interval at which the interceptor
// will send new feedback reports.
func SendInterval(interval time.Duration) Option {
return func(s *SenderInterceptor) error {
s.interval = interval
return nil
}
}
// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
// will be called once per packet batch.
func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
s.m.Lock()
defer s.m.Unlock()
s.recorder = NewRecorder(rand.Uint32()) // #nosec
if s.isClosed() {
return writer
}
s.wg.Add(1)
go s.loop(writer)
return writer
}
type packet struct {
hdr *rtp.Header
sequenceNumber uint16
arrivalTime int64
ssrc uint32
}
// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
// will be called once per rtp packet.
func (s *SenderInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
var hdrExtID uint8
for _, e := range info.RTPHeaderExtensions {
if e.URI == transportCCURI {
hdrExtID = uint8(e.ID)
break
}
}
if hdrExtID == 0 { // Don't try to read header extension if ID is 0, because 0 is an invalid extension ID
return reader
}
return interceptor.RTPReaderFunc(func(buf []byte, attributes interceptor.Attributes) (int, interceptor.Attributes, error) {
i, attr, err := reader.Read(buf, attributes)
if err != nil {
return 0, nil, err
}
if attr == nil {
attr = make(interceptor.Attributes)
}
header, err := attr.GetRTPHeader(buf[:i])
if err != nil {
return 0, nil, err
}
var tccExt rtp.TransportCCExtension
if ext := header.GetExtension(hdrExtID); ext != nil {
err = tccExt.Unmarshal(ext)
if err != nil {
return 0, nil, err
}
s.packetChan <- packet{
hdr: header,
sequenceNumber: tccExt.TransportSequence,
arrivalTime: time.Since(s.startTime).Microseconds(),
ssrc: info.SSRC,
}
}
return i, attr, nil
})
}
// Close closes the interceptor.
func (s *SenderInterceptor) Close() error {
defer s.wg.Wait()
s.m.Lock()
defer s.m.Unlock()
if !s.isClosed() {
close(s.close)
}
return nil
}
func (s *SenderInterceptor) isClosed() bool {
select {
case <-s.close:
return true
default:
return false
}
}
func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) {
defer s.wg.Done()
ticker := time.NewTicker(s.interval)
for {
select {
case <-s.close:
ticker.Stop()
return
case p := <-s.packetChan:
s.recorder.Record(p.ssrc, p.sequenceNumber, p.arrivalTime)
case <-ticker.C:
// build and send twcc
pkts := s.recorder.BuildFeedbackPacket()
if pkts == nil {
continue
}
if _, err := w.Write(pkts, nil); err != nil {
s.log.Error(err.Error())
}
}
}
}

274
vendor/github.com/pion/interceptor/pkg/twcc/twcc.go generated vendored Normal file
View File

@@ -0,0 +1,274 @@
// Package twcc provides interceptors to implement transport wide congestion control.
package twcc
import (
"math"
"github.com/pion/rtcp"
)
type pktInfo struct {
sequenceNumber uint32
arrivalTime int64
}
// Recorder records incoming RTP packets and their delays and creates
// transport wide congestion control feedback reports as specified in
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
type Recorder struct {
receivedPackets []pktInfo
cycles uint32
lastSequenceNumber uint16
senderSSRC uint32
mediaSSRC uint32
fbPktCnt uint8
}
// NewRecorder creates a new Recorder which uses the given senderSSRC in the created
// feedback packets.
func NewRecorder(senderSSRC uint32) *Recorder {
return &Recorder{
receivedPackets: []pktInfo{},
senderSSRC: senderSSRC,
}
}
// Record marks a packet with mediaSSRC and a transport wide sequence number sequenceNumber as received at arrivalTime.
func (r *Recorder) Record(mediaSSRC uint32, sequenceNumber uint16, arrivalTime int64) {
r.mediaSSRC = mediaSSRC
if sequenceNumber < 0x0fff && (r.lastSequenceNumber&0xffff) > 0xf000 {
r.cycles += 1 << 16
}
r.receivedPackets = insertSorted(r.receivedPackets, pktInfo{
sequenceNumber: r.cycles | uint32(sequenceNumber),
arrivalTime: arrivalTime,
})
r.lastSequenceNumber = sequenceNumber
}
func insertSorted(list []pktInfo, element pktInfo) []pktInfo {
if len(list) == 0 {
return append(list, element)
}
for i := len(list) - 1; i >= 0; i-- {
if list[i].sequenceNumber < element.sequenceNumber {
list = append(list, pktInfo{})
copy(list[i+2:], list[i+1:])
list[i+1] = element
return list
}
if list[i].sequenceNumber == element.sequenceNumber {
list[i] = element
return list
}
}
// element.sequenceNumber is between 0 and first ever received sequenceNumber
return append([]pktInfo{element}, list...)
}
// BuildFeedbackPacket creates a new RTCP packet containing a TWCC feedback report.
func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet {
feedback := newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
r.fbPktCnt++
if len(r.receivedPackets) < 2 {
r.receivedPackets = []pktInfo{}
return []rtcp.Packet{feedback.getRTCP()}
}
feedback.setBase(uint16(r.receivedPackets[0].sequenceNumber&0xffff), r.receivedPackets[0].arrivalTime)
var pkts []rtcp.Packet
for _, pkt := range r.receivedPackets {
ok := feedback.addReceived(uint16(pkt.sequenceNumber&0xffff), pkt.arrivalTime)
if !ok {
pkts = append(pkts, feedback.getRTCP())
feedback = newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt)
r.fbPktCnt++
feedback.addReceived(uint16(pkt.sequenceNumber&0xffff), pkt.arrivalTime)
}
}
r.receivedPackets = []pktInfo{}
pkts = append(pkts, feedback.getRTCP())
return pkts
}
type feedback struct {
rtcp *rtcp.TransportLayerCC
baseSequenceNumber uint16
refTimestamp64MS int64
lastTimestampUS int64
nextSequenceNumber uint16
sequenceNumberCount uint16
len int
lastChunk chunk
chunks []rtcp.PacketStatusChunk
deltas []*rtcp.RecvDelta
}
func newFeedback(senderSSRC, mediaSSRC uint32, count uint8) *feedback {
return &feedback{
rtcp: &rtcp.TransportLayerCC{
SenderSSRC: senderSSRC,
MediaSSRC: mediaSSRC,
FbPktCount: count,
},
}
}
func (f *feedback) setBase(sequenceNumber uint16, timeUS int64) {
f.baseSequenceNumber = sequenceNumber
f.nextSequenceNumber = f.baseSequenceNumber
f.refTimestamp64MS = timeUS / 64e3
f.lastTimestampUS = f.refTimestamp64MS * 64e3
}
func (f *feedback) getRTCP() *rtcp.TransportLayerCC {
f.rtcp.PacketStatusCount = f.sequenceNumberCount
f.rtcp.ReferenceTime = uint32(f.refTimestamp64MS)
f.rtcp.BaseSequenceNumber = f.baseSequenceNumber
for len(f.lastChunk.deltas) > 0 {
f.chunks = append(f.chunks, f.lastChunk.encode())
}
f.rtcp.PacketChunks = append(f.rtcp.PacketChunks, f.chunks...)
f.rtcp.RecvDeltas = f.deltas
padLen := 20 + len(f.rtcp.PacketChunks)*2 + f.len // 4 bytes header + 16 bytes twcc header + 2 bytes for each chunk + length of deltas
padding := padLen%4 != 0
for padLen%4 != 0 {
padLen++
}
f.rtcp.Header = rtcp.Header{
Count: rtcp.FormatTCC,
Type: rtcp.TypeTransportSpecificFeedback,
Padding: padding,
Length: uint16((padLen / 4) - 1),
}
return f.rtcp
}
func (f *feedback) addReceived(sequenceNumber uint16, timestampUS int64) bool {
deltaUS := timestampUS - f.lastTimestampUS
delta250US := deltaUS / 250
if delta250US < math.MinInt16 || delta250US > math.MaxInt16 { // delta doesn't fit into 16 bit, need to create new packet
return false
}
for ; f.nextSequenceNumber != sequenceNumber; f.nextSequenceNumber++ {
if !f.lastChunk.canAdd(rtcp.TypeTCCPacketNotReceived) {
f.chunks = append(f.chunks, f.lastChunk.encode())
}
f.lastChunk.add(rtcp.TypeTCCPacketNotReceived)
f.sequenceNumberCount++
}
var recvDelta uint16
switch {
case delta250US >= 0 && delta250US <= 0xff:
f.len++
recvDelta = rtcp.TypeTCCPacketReceivedSmallDelta
default:
f.len += 2
recvDelta = rtcp.TypeTCCPacketReceivedLargeDelta
}
if !f.lastChunk.canAdd(recvDelta) {
f.chunks = append(f.chunks, f.lastChunk.encode())
}
f.lastChunk.add(recvDelta)
f.deltas = append(f.deltas, &rtcp.RecvDelta{
Type: recvDelta,
Delta: deltaUS,
})
f.lastTimestampUS = timestampUS
f.sequenceNumberCount++
f.nextSequenceNumber++
return true
}
const (
maxRunLengthCap = 0x1fff // 13 bits
maxOneBitCap = 14 // bits
maxTwoBitCap = 7 // bits
)
type chunk struct {
hasLargeDelta bool
hasDifferentTypes bool
deltas []uint16
}
func (c *chunk) canAdd(delta uint16) bool {
if len(c.deltas) < maxTwoBitCap {
return true
}
if len(c.deltas) < maxOneBitCap && !c.hasLargeDelta && delta != rtcp.TypeTCCPacketReceivedLargeDelta {
return true
}
if len(c.deltas) < maxRunLengthCap && !c.hasDifferentTypes && delta == c.deltas[0] {
return true
}
return false
}
func (c *chunk) add(delta uint16) {
c.deltas = append(c.deltas, delta)
c.hasLargeDelta = c.hasLargeDelta || delta == rtcp.TypeTCCPacketReceivedLargeDelta
c.hasDifferentTypes = c.hasDifferentTypes || delta != c.deltas[0]
}
func (c *chunk) encode() rtcp.PacketStatusChunk {
if !c.hasDifferentTypes {
defer c.reset()
return &rtcp.RunLengthChunk{
PacketStatusSymbol: c.deltas[0],
RunLength: uint16(len(c.deltas)),
}
}
if len(c.deltas) == maxOneBitCap {
defer c.reset()
return &rtcp.StatusVectorChunk{
SymbolSize: rtcp.TypeTCCSymbolSizeOneBit,
SymbolList: c.deltas,
}
}
minCap := min(maxTwoBitCap, len(c.deltas))
svc := &rtcp.StatusVectorChunk{
SymbolSize: rtcp.TypeTCCSymbolSizeTwoBit,
SymbolList: c.deltas[:minCap],
}
c.deltas = c.deltas[minCap:]
c.hasDifferentTypes = false
c.hasLargeDelta = false
if len(c.deltas) > 0 {
tmp := c.deltas[0]
for _, d := range c.deltas {
if tmp != d {
c.hasDifferentTypes = true
}
if d == rtcp.TypeTCCPacketReceivedLargeDelta {
c.hasLargeDelta = true
}
}
}
return svc
}
func (c *chunk) reset() {
c.deltas = []uint16{}
c.hasLargeDelta = false
c.hasDifferentTypes = false
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

30
vendor/github.com/pion/interceptor/registry.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package interceptor
// Registry is a collector for interceptors.
type Registry struct {
factories []Factory
}
// Add adds a new Interceptor to the registry.
func (r *Registry) Add(f Factory) {
r.factories = append(r.factories, f)
}
// Build constructs a single Interceptor from a InterceptorRegistry
func (r *Registry) Build(id string) (Interceptor, error) {
if len(r.factories) == 0 {
return &NoOp{}, nil
}
interceptors := []Interceptor{}
for _, f := range r.factories {
i, err := f.NewInterceptor(id)
if err != nil {
return nil, err
}
interceptors = append(interceptors, i)
}
return NewChain(interceptors), nil
}

27
vendor/github.com/pion/interceptor/renovate.json generated vendored Normal file
View File

@@ -0,0 +1,27 @@
{
"extends": [
"config:base",
":disableDependencyDashboard"
],
"postUpdateOptions": [
"gomodTidy"
],
"commitBody": "Generated by renovateBot",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
},
{
"packagePatterns": ["^golang.org/x/"],
"schedule": ["on the first day of the month"]
}
],
"ignorePaths": [
".github/workflows/generate-authors.yml",
".github/workflows/lint.yaml",
".github/workflows/renovate-go-mod-fix.yaml",
".github/workflows/test.yaml",
".github/workflows/tidy-check.yaml"
]
}

34
vendor/github.com/pion/interceptor/streaminfo.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
package interceptor
// RTPHeaderExtension represents a negotiated RFC5285 RTP header extension.
type RTPHeaderExtension struct {
URI string
ID int
}
// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded
type StreamInfo struct {
ID string
Attributes Attributes
SSRC uint32
PayloadType uint8
RTPHeaderExtensions []RTPHeaderExtension
MimeType string
ClockRate uint32
Channels uint16
SDPFmtpLine string
RTCPFeedback []RTCPFeedback
}
// RTCPFeedback signals the connection to use additional RTCP packet types.
// https://draft.ortc.org/#dom-rtcrtcpfeedback
type RTCPFeedback struct {
// Type is the type of feedback.
// see: https://draft.ortc.org/#dom-rtcrtcpfeedback
// valid: ack, ccm, nack, goog-remb, transport-cc
Type string
// The parameter value depends on the type.
// For example, type="nack" parameter="pli" will send Picture Loss Indicator packets.
Parameter string
}