1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

TEST: Upgrade pion to v3.2.9. (#3567)

------

Co-authored-by: chundonglinlin <chundonglinlin@163.com>
This commit is contained in:
Winlin 2023-06-05 11:25:04 +08:00 committed by GitHub
parent 104cf14d68
commit df854339ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1383 changed files with 118469 additions and 41421 deletions

View file

@ -22,3 +22,4 @@ cover.out
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js

View file

@ -15,14 +15,22 @@ linters-settings:
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- contextcheck # check the function whether use a non-inherited context
- deadcode # Finds unused code
- decorder # check declaration order and count of types, constants, variables and functions
- depguard # Go linter that checks if package imports are in a list of acceptable packages
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- exhaustive # check exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
- forcetypeassert # finds forced type assertions
- gci # Gci control golang package import order and make it always deterministic.
- gochecknoglobals # Checks that no globals are present in Go code
- gochecknoinits # Checks that no init functions are present in Go code
@ -35,40 +43,62 @@ linters:
- 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
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
- gosec # Inspects source code for security problems
- gosimple # Linter for Go source code that specializes in simplifying a code
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- grouper # An analyzer to analyze expression groups.
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
- misspell # Finds commonly misspelled English words in comments
- nakedret # Finds naked returns in functions greater than a specified function length
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- noctx # noctx finds sending http request without context.Context
- scopelint # Scopelint checks for unpinned variables in go programs
- predeclared # find code that shadows one of Go's predeclared identifiers
- revive # golint replacement, finds style mistakes
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- structcheck # Finds unused struct fields
- stylecheck # Stylecheck is a replacement for golint
- tagliatelle # Checks the struct tags.
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
- varcheck # Finds unused global variables and constants
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
- containedctx # containedctx is a linter that detects struct contained context.Context field
- cyclop # checks function and package cyclomatic complexity
- exhaustivestruct # Checks if all struct's fields are initialized
- forbidigo # Forbids identifiers
- funlen # Tool for detection of long functions
- gocyclo # Computes and checks the cyclomatic complexity of functions
- godot # Check if comments end in a period
- gomnd # An analyzer to detect magic numbers.
- ifshort # Checks that your code uses short syntax for if-statements whenever possible
- ireturn # Accept Interfaces, Return Concrete Types
- lll # Reports long lines
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
- nestif # Reports deeply nested if statements
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- nolintlint # Reports ill-formed or insufficient nolint directives
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- prealloc # Finds slice declarations that could potentially be preallocated
- promlinter # Check Prometheus metrics naming via promlint
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- testpackage # linter that makes you use a separate _test package
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- varnamelen # checks that the length of a variable's name matches its scope
- wrapcheck # Checks that errors returned from external packages are wrapped
- wsl # Whitespace Linter - Forces you to use empty lines!
issues:

View file

@ -0,0 +1,23 @@
# Thank you to everyone that made Pion possible. If you are interested in contributing
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
#
# This file is auto generated, using git to list all individuals contributors.
# see `.github/generate-authors.sh` for the scripting
Adam Roach <adam@caffeine.tv>
adwpc <adwpc@hotmail.com>
aggresss <aggresss@163.com>
Atsushi Watanabe <atsushi.w@ieee.org>
cnderrauber <zengjie9004@gmail.com>
Gabor Pongracz <gabor.pongracz@proemergotech.com>
Hugo Arregui <hugo.arregui@gmail.com>
Hugo Arregui <hugo@decentraland.org>
Juliusz Chroboczek <jch@irif.fr>
Kevin Wang <kevmo314@gmail.com>
lllf <littlelightlittlefire@gmail.com>
Luke Curley <kixelated@gmail.com>
Mathis Engelbart <mathis.engelbart@gmail.com>
Max Hawkins <maxhawkins@gmail.com>
Sean DuBois <seaduboi@amazon.com>
Sean DuBois <sean@siobud.com>
Simone Gotti <simone.gotti@gmail.com>
Woodrow Douglass <wdouglass@carnegierobotics.com>

View file

@ -32,18 +32,5 @@ If you need commercial support or don't want to use public methods you can conta
### Contributing
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
* [Max Hawkins](https://github.com/maxhawkins) - *Original Author*
* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
* [Sean DuBois](https://github.com/Sean-Der) - *Linter fixes*
* [adwpc](https://github.com/adwpc)
* [Luke Curley](https://github.com/kixelated)
* [Hugo Arregui](https://github.com/hugoArregui)
* [Atsushi Watanabe](https://github.com/at-wat)
* [Juliusz Chroboczek](https://github.com/jech)
* [Gabor Pongracz](https://github.com/pongraczgabor87)
* [Simone Gotti](https://github.com/sgotti)
* [lllf](https://github.com/LittleLightLittleFire)
* [cnderrauber](https://github.com/cnderrauber)
### License
MIT License - see [LICENSE](LICENSE) for full text

View file

@ -1,5 +1,10 @@
package rtcp
import (
"fmt"
"strings"
)
// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
// the underlying protocol (for example UDP).
//
@ -14,8 +19,6 @@ package rtcp
// Other RTCP packet types may follow in any order. Packet types may appear more than once.
type CompoundPacket []Packet
var _ Packet = (*CompoundPacket)(nil) // assert is a Packet
// Validate returns an error if this is not an RFC-compliant CompoundPacket.
func (c CompoundPacket) Validate() error {
if len(c) == 0 {
@ -134,3 +137,17 @@ func (c CompoundPacket) DestinationSSRC() []uint32 {
return c[0].DestinationSSRC()
}
func (c CompoundPacket) String() string {
out := "CompoundPacket\n"
for _, p := range c {
stringer, canString := p.(fmt.Stringer)
if canString {
out += stringer.String()
} else {
out += stringify(p)
}
}
out = strings.TrimSuffix(strings.ReplaceAll(out, "\n", "\n\t"), "\t")
return out
}

View file

@ -14,16 +14,17 @@ service parameters, perhaps by limiting flow, or using a different codec.
Decoding RTCP packets:
pkt, err := rtcp.Unmarshal(rtcpData)
pkts, err := rtcp.Unmarshal(rtcpData)
// ...
switch p := pkt.(type) {
case *rtcp.CompoundPacket:
...
case *rtcp.PictureLossIndication:
...
default:
...
for _, pkt := range pkts {
switch p := pkt.(type) {
case *rtcp.CompoundPacket:
...
case *rtcp.PictureLossIndication:
...
default:
...
}
}
Encoding RTCP packets:

View file

@ -27,4 +27,8 @@ var (
errMissingREMBidentifier = errors.New("missing REMB identifier")
errSSRCNumAndLengthMismatch = errors.New("SSRC num and length do not match")
errInvalidSizeOrStartIndex = errors.New("invalid size or startIndex")
errInvalidBitrate = errors.New("invalid bitrate")
errWrongChunkType = errors.New("rtcp: wrong chunk type")
errBadStructMemberType = errors.New("rtcp: struct contains unexpected member type")
errBadReadParameter = errors.New("rtcp: cannot read into non-pointer")
)

View file

@ -0,0 +1,643 @@
package rtcp
import (
"fmt"
)
// The ExtendedReport packet is an Implementation of RTCP Extended
// Reports defined in RFC 3611. It is used to convey detailed
// information about an RTP stream. Each packet contains one or
// more report blocks, each of which conveys a different kind of
// information.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|reserved | PT=XR=207 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : report blocks :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type ExtendedReport struct {
SenderSSRC uint32 `fmt:"0x%X"`
Reports []ReportBlock
}
// ReportBlock represents a single report within an ExtendedReport
// packet
type ReportBlock interface {
DestinationSSRC() []uint32
setupBlockHeader()
unpackBlockHeader()
}
// TypeSpecificField as described in RFC 3611 section 4.5. In typical
// cases, users of ExtendedReports shouldn't need to access this,
// and should instead use the corresponding fields in the actual
// report blocks themselves.
type TypeSpecificField uint8
// XRHeader defines the common fields that must appear at the start
// of each report block. In typical cases, users of ExtendedReports
// shouldn't need to access this. For locally-constructed report
// blocks, these values will not be accurate until the corresponding
// packet is marshaled.
type XRHeader struct {
BlockType BlockTypeType
TypeSpecific TypeSpecificField `fmt:"0x%X"`
BlockLength uint16
}
// BlockTypeType specifies the type of report in a report block
type BlockTypeType uint8
// Extended Report block types from RFC 3611.
const (
LossRLEReportBlockType = 1 // RFC 3611, section 4.1
DuplicateRLEReportBlockType = 2 // RFC 3611, section 4.2
PacketReceiptTimesReportBlockType = 3 // RFC 3611, section 4.3
ReceiverReferenceTimeReportBlockType = 4 // RFC 3611, section 4.4
DLRRReportBlockType = 5 // RFC 3611, section 4.5
StatisticsSummaryReportBlockType = 6 // RFC 3611, section 4.6
VoIPMetricsReportBlockType = 7 // RFC 3611, section 4.7
)
// String converts the Extended report block types into readable strings
func (t BlockTypeType) String() string {
switch t {
case LossRLEReportBlockType:
return "LossRLEReportBlockType"
case DuplicateRLEReportBlockType:
return "DuplicateRLEReportBlockType"
case PacketReceiptTimesReportBlockType:
return "PacketReceiptTimesReportBlockType"
case ReceiverReferenceTimeReportBlockType:
return "ReceiverReferenceTimeReportBlockType"
case DLRRReportBlockType:
return "DLRRReportBlockType"
case StatisticsSummaryReportBlockType:
return "StatisticsSummaryReportBlockType"
case VoIPMetricsReportBlockType:
return "VoIPMetricsReportBlockType"
}
return fmt.Sprintf("invalid value %d", t)
}
// rleReportBlock defines the common structure used by both
// Loss RLE report blocks (RFC 3611 §4.1) and Duplicate RLE
// report blocks (RFC 3611 §4.2).
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT = 1 or 2 | rsvd. | T | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | end_seq |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | chunk 1 | chunk 2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | chunk n-1 | chunk n |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type rleReportBlock struct {
XRHeader
T uint8 `encoding:"omit"`
SSRC uint32 `fmt:"0x%X"`
BeginSeq uint16
EndSeq uint16
Chunks []Chunk
}
// Chunk as defined in RFC 3611, section 4.1. These represent information
// about packet losses and packet duplication. They have three representations:
//
// Run Length Chunk:
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |C|R| run length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Bit Vector Chunk:
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |C| bit vector |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Terminating Null Chunk:
//
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type Chunk uint16
// LossRLEReportBlock is used to report information about packet
// losses, as described in RFC 3611, section 4.1
type LossRLEReportBlock rleReportBlock
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *LossRLEReportBlock) DestinationSSRC() []uint32 {
return []uint32{b.SSRC}
}
func (b *LossRLEReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = LossRLEReportBlockType
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *LossRLEReportBlock) unpackBlockHeader() {
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
}
// DuplicateRLEReportBlock is used to report information about packet
// duplication, as described in RFC 3611, section 4.1
type DuplicateRLEReportBlock rleReportBlock
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *DuplicateRLEReportBlock) DestinationSSRC() []uint32 {
return []uint32{b.SSRC}
}
func (b *DuplicateRLEReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = DuplicateRLEReportBlockType
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *DuplicateRLEReportBlock) unpackBlockHeader() {
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
}
// ChunkType enumerates the three kinds of chunks described in RFC 3611 section 4.1.
type ChunkType uint8
// These are the valid values that ChunkType can assume
const (
RunLengthChunkType = 0
BitVectorChunkType = 1
TerminatingNullChunkType = 2
)
func (c Chunk) String() string {
switch c.Type() {
case RunLengthChunkType:
runType, _ := c.RunType()
return fmt.Sprintf("[RunLength type=%d, length=%d]", runType, c.Value())
case BitVectorChunkType:
return fmt.Sprintf("[BitVector 0b%015b]", c.Value())
case TerminatingNullChunkType:
return "[TerminatingNull]"
}
return fmt.Sprintf("[0x%X]", uint16(c))
}
// Type returns the ChunkType that this Chunk represents
func (c Chunk) Type() ChunkType {
if c == 0 {
return TerminatingNullChunkType
}
return ChunkType(c >> 15)
}
// RunType returns the RunType that this Chunk represents. It is
// only valid if ChunkType is RunLengthChunkType.
func (c Chunk) RunType() (uint, error) {
if c.Type() != RunLengthChunkType {
return 0, errWrongChunkType
}
return uint((c >> 14) & 0x01), nil
}
// Value returns the value represented in this Chunk
func (c Chunk) Value() uint {
switch c.Type() {
case RunLengthChunkType:
return uint(c & 0x3FFF)
case BitVectorChunkType:
return uint(c & 0x7FFF)
case TerminatingNullChunkType:
return 0
}
return uint(c)
}
// PacketReceiptTimesReportBlock represents a Packet Receipt Times
// report block, as described in RFC 3611 section 4.3.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=3 | rsvd. | T | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | end_seq |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Receipt time of packet begin_seq |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Receipt time of packet (begin_seq + 1) mod 65536 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : ... :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Receipt time of packet (end_seq - 1) mod 65536 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type PacketReceiptTimesReportBlock struct {
XRHeader
T uint8 `encoding:"omit"`
SSRC uint32 `fmt:"0x%X"`
BeginSeq uint16
EndSeq uint16
ReceiptTime []uint32
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *PacketReceiptTimesReportBlock) DestinationSSRC() []uint32 {
return []uint32{b.SSRC}
}
func (b *PacketReceiptTimesReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = PacketReceiptTimesReportBlockType
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *PacketReceiptTimesReportBlock) unpackBlockHeader() {
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
}
// ReceiverReferenceTimeReportBlock encodes a Receiver Reference Time
// report block as described in RFC 3611 section 4.4.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=4 | reserved | block length = 2 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, most significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NTP timestamp, least significant word |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type ReceiverReferenceTimeReportBlock struct {
XRHeader
NTPTimestamp uint64
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *ReceiverReferenceTimeReportBlock) DestinationSSRC() []uint32 {
return []uint32{}
}
func (b *ReceiverReferenceTimeReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = ReceiverReferenceTimeReportBlockType
b.XRHeader.TypeSpecific = 0
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *ReceiverReferenceTimeReportBlock) unpackBlockHeader() {
}
// DLRRReportBlock encodes a DLRR Report Block as described in
// RFC 3611 section 4.5.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=5 | reserved | block length |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_1 (SSRC of first receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// | last RR (LRR) | 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | delay since last RR (DLRR) |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | SSRC_2 (SSRC of second receiver) | sub-
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
// : ... : 2
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
type DLRRReportBlock struct {
XRHeader
Reports []DLRRReport
}
// DLRRReport encodes a single report inside a DLRRReportBlock.
type DLRRReport struct {
SSRC uint32 `fmt:"0x%X"`
LastRR uint32
DLRR uint32
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *DLRRReportBlock) DestinationSSRC() []uint32 {
ssrc := make([]uint32, len(b.Reports))
for i, r := range b.Reports {
ssrc[i] = r.SSRC
}
return ssrc
}
func (b *DLRRReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = DLRRReportBlockType
b.XRHeader.TypeSpecific = 0
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *DLRRReportBlock) unpackBlockHeader() {
}
// StatisticsSummaryReportBlock encodes a Statistics Summary Report
// Block as described in RFC 3611, section 4.6.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=6 |L|D|J|ToH|rsvd.| block length = 9 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | end_seq |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | lost_packets |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | dup_packets |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | min_jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | max_jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | mean_jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | dev_jitter |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | min_ttl_or_hl | max_ttl_or_hl |mean_ttl_or_hl | dev_ttl_or_hl |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type StatisticsSummaryReportBlock struct {
XRHeader
LossReports bool `encoding:"omit"`
DuplicateReports bool `encoding:"omit"`
JitterReports bool `encoding:"omit"`
TTLorHopLimit TTLorHopLimitType `encoding:"omit"`
SSRC uint32 `fmt:"0x%X"`
BeginSeq uint16
EndSeq uint16
LostPackets uint32
DupPackets uint32
MinJitter uint32
MaxJitter uint32
MeanJitter uint32
DevJitter uint32
MinTTLOrHL uint8
MaxTTLOrHL uint8
MeanTTLOrHL uint8
DevTTLOrHL uint8
}
// TTLorHopLimitType encodes values for the ToH field in
// a StatisticsSummaryReportBlock
type TTLorHopLimitType uint8
// Values for TTLorHopLimitType
const (
ToHMissing = 0
ToHIPv4 = 1
ToHIPv6 = 2
)
func (t TTLorHopLimitType) String() string {
switch t {
case ToHMissing:
return "[ToH Missing]"
case ToHIPv4:
return "[ToH = IPv4]"
case ToHIPv6:
return "[ToH = IPv6]"
}
return "[ToH Flag is Invalid]"
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *StatisticsSummaryReportBlock) DestinationSSRC() []uint32 {
return []uint32{b.SSRC}
}
func (b *StatisticsSummaryReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = StatisticsSummaryReportBlockType
b.XRHeader.TypeSpecific = 0x00
if b.LossReports {
b.XRHeader.TypeSpecific |= 0x80
}
if b.DuplicateReports {
b.XRHeader.TypeSpecific |= 0x40
}
if b.JitterReports {
b.XRHeader.TypeSpecific |= 0x20
}
b.XRHeader.TypeSpecific |= TypeSpecificField((b.TTLorHopLimit & 0x03) << 3)
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *StatisticsSummaryReportBlock) unpackBlockHeader() {
b.LossReports = b.XRHeader.TypeSpecific&0x80 != 0
b.DuplicateReports = b.XRHeader.TypeSpecific&0x40 != 0
b.JitterReports = b.XRHeader.TypeSpecific&0x20 != 0
b.TTLorHopLimit = TTLorHopLimitType((b.XRHeader.TypeSpecific & 0x18) >> 3)
}
// VoIPMetricsReportBlock encodes a VoIP Metrics Report Block as described
// in RFC 3611, section 4.7.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | BT=7 | reserved | block length = 8 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | loss rate | discard rate | burst density | gap density |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | burst duration | gap duration |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | round trip delay | end system delay |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | signal level | noise level | RERL | Gmin |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | R factor | ext. R factor | MOS-LQ | MOS-CQ |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RX config | reserved | JB nominal |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | JB maximum | JB abs max |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type VoIPMetricsReportBlock struct {
XRHeader
SSRC uint32 `fmt:"0x%X"`
LossRate uint8
DiscardRate uint8
BurstDensity uint8
GapDensity uint8
BurstDuration uint16
GapDuration uint16
RoundTripDelay uint16
EndSystemDelay uint16
SignalLevel uint8
NoiseLevel uint8
RERL uint8
Gmin uint8
RFactor uint8
ExtRFactor uint8
MOSLQ uint8
MOSCQ uint8
RXConfig uint8
_ uint8
JBNominal uint16
JBMaximum uint16
JBAbsMax uint16
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *VoIPMetricsReportBlock) DestinationSSRC() []uint32 {
return []uint32{b.SSRC}
}
func (b *VoIPMetricsReportBlock) setupBlockHeader() {
b.XRHeader.BlockType = VoIPMetricsReportBlockType
b.XRHeader.TypeSpecific = 0
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *VoIPMetricsReportBlock) unpackBlockHeader() {
}
// UnknownReportBlock is used to store bytes for any report block
// that has an unknown Report Block Type.
type UnknownReportBlock struct {
XRHeader
Bytes []byte
}
// DestinationSSRC returns an array of SSRC values that this report block refers to.
func (b *UnknownReportBlock) DestinationSSRC() []uint32 {
return []uint32{}
}
func (b *UnknownReportBlock) setupBlockHeader() {
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
}
func (b *UnknownReportBlock) unpackBlockHeader() {
}
// Marshal encodes the ExtendedReport in binary
func (x ExtendedReport) Marshal() ([]byte, error) {
for _, p := range x.Reports {
p.setupBlockHeader()
}
length := wireSize(x)
// RTCP Header
header := Header{
Type: TypeExtendedReport,
Length: uint16(length / 4),
}
headerBuffer, err := header.Marshal()
if err != nil {
return []byte{}, err
}
length += len(headerBuffer)
rawPacket := make([]byte, length)
buffer := packetBuffer{bytes: rawPacket}
err = buffer.write(headerBuffer)
if err != nil {
return []byte{}, err
}
err = buffer.write(x)
if err != nil {
return []byte{}, err
}
return rawPacket, nil
}
// Unmarshal decodes the ExtendedReport from binary
func (x *ExtendedReport) Unmarshal(b []byte) error {
var header Header
if err := header.Unmarshal(b); err != nil {
return err
}
if header.Type != TypeExtendedReport {
return errWrongType
}
buffer := packetBuffer{bytes: b[headerLength:]}
err := buffer.read(&x.SenderSSRC)
if err != nil {
return err
}
for len(buffer.bytes) > 0 {
var block ReportBlock
headerBuffer := buffer
xrHeader := XRHeader{}
err = headerBuffer.read(&xrHeader)
if err != nil {
return err
}
switch xrHeader.BlockType {
case LossRLEReportBlockType:
block = new(LossRLEReportBlock)
case DuplicateRLEReportBlockType:
block = new(DuplicateRLEReportBlock)
case PacketReceiptTimesReportBlockType:
block = new(PacketReceiptTimesReportBlock)
case ReceiverReferenceTimeReportBlockType:
block = new(ReceiverReferenceTimeReportBlock)
case DLRRReportBlockType:
block = new(DLRRReportBlock)
case StatisticsSummaryReportBlockType:
block = new(StatisticsSummaryReportBlock)
case VoIPMetricsReportBlockType:
block = new(VoIPMetricsReportBlock)
default:
block = new(UnknownReportBlock)
}
// We need to limit the amount of data available to
// this block to the actual length of the block
blockLength := (int(xrHeader.BlockLength) + 1) * 4
blockBuffer := buffer.split(blockLength)
err = blockBuffer.read(block)
if err != nil {
return err
}
block.unpackBlockHeader()
x.Reports = append(x.Reports, block)
}
return nil
}
// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (x *ExtendedReport) DestinationSSRC() []uint32 {
ssrc := make([]uint32, 0)
for _, p := range x.Reports {
ssrc = append(ssrc, p.DestinationSSRC()...)
}
return ssrc
}
func (x *ExtendedReport) String() string {
return stringify(x)
}

View file

@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz
package rtcp

View file

@ -1,5 +0,0 @@
module github.com/pion/rtcp
go 1.13
require github.com/stretchr/testify v1.6.1

View file

@ -1,11 +0,0 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -2,6 +2,7 @@ package rtcp
import (
"encoding/binary"
"fmt"
)
// The Goodbye packet indicates that one or more sources are no longer active.
@ -12,8 +13,6 @@ type Goodbye struct {
Reason string
}
var _ Packet = (*Goodbye)(nil) // assert is a Packet
// Marshal encodes the Goodbye packet in binary
func (g Goodbye) Marshal() ([]byte, error) {
/*
@ -144,3 +143,13 @@ func (g *Goodbye) DestinationSSRC() []uint32 {
copy(out, g.Sources)
return out
}
func (g Goodbye) String() string {
out := "Goodbye\n"
for i, s := range g.Sources {
out += fmt.Sprintf("\tSource %d: %x\n", i, s)
}
out += fmt.Sprintf("\tReason: %s\n", g.Reason)
return out
}

View file

@ -16,6 +16,7 @@ const (
TypeApplicationDefined PacketType = 204 // RFC 3550, 6.7 (unimplemented)
TypeTransportSpecificFeedback PacketType = 205 // RFC 4585, 6051
TypePayloadSpecificFeedback PacketType = 206 // RFC 4585, 6.3
TypeExtendedReport PacketType = 207 // RFC 3611
)
@ -26,9 +27,10 @@ const (
FormatFIR uint8 = 4
FormatTLN uint8 = 1
FormatRRR uint8 = 5
FormatCCFB uint8 = 11
FormatREMB uint8 = 15
//https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
FormatTCC uint8 = 15
)
@ -48,6 +50,8 @@ func (p PacketType) String() string {
return "TSFB"
case TypePayloadSpecificFeedback:
return "PSFB"
case TypeExtendedReport:
return "XR"
default:
return string(p)
}

View file

@ -87,6 +87,8 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {
packet = new(RapidResynchronizationRequest)
case FormatTCC:
packet = new(TransportLayerCC)
case FormatCCFB:
packet = new(CCFeedbackReport)
default:
packet = new(RawPacket)
}
@ -105,6 +107,9 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {
packet = new(RawPacket)
}
case TypeExtendedReport:
packet = new(ExtendedReport)
default:
packet = new(RawPacket)
}

View file

@ -0,0 +1,256 @@
package rtcp
import (
"encoding/binary"
"reflect"
"unsafe"
)
// These functions implement an introspective structure
// serializer/deserializer, designed to allow RTCP packet
// Structs to be self-describing. They currently work with
// fields of type uint8, uint16, uint32, and uint64 (and
// types derived from them).
//
// - Unexported fields will take up space in the encoded
// array, but wil be set to zero when written, and ignore
// when read.
//
// - Fields that are marked with the tag `encoding:"omit"`
// will be ignored when reading and writing data.
//
// For example:
//
// type Example struct {
// A uint32
// B bool `encoding:"omit"`
// _ uint64
// C uint16
// }
//
// "A" will be encoded as four bytes, in network order. "B"
// will not be encoded at all. The anonymous uint64 will
// encode as 8 bytes of value "0", followed by two bytes
// encoding "C" in network order.
type packetBuffer struct {
bytes []byte
}
const omit = "omit"
// Writes the structure passed to into the buffer that
// PacketBuffer is initialized with. This function will
// modify the PacketBuffer.bytes slice to exclude those
// bytes that have been written into.
//
func (b *packetBuffer) write(v interface{}) error { //nolint:gocognit
value := reflect.ValueOf(v)
// Indirect is safe to call on non-pointers, and
// will simply return the same value in such cases
value = reflect.Indirect(value)
switch value.Kind() {
case reflect.Uint8:
if len(b.bytes) < 1 {
return errWrongMarshalSize
}
if value.CanInterface() {
b.bytes[0] = byte(value.Uint())
}
b.bytes = b.bytes[1:]
case reflect.Uint16:
if len(b.bytes) < 2 {
return errWrongMarshalSize
}
if value.CanInterface() {
binary.BigEndian.PutUint16(b.bytes, uint16(value.Uint()))
}
b.bytes = b.bytes[2:]
case reflect.Uint32:
if len(b.bytes) < 4 {
return errWrongMarshalSize
}
if value.CanInterface() {
binary.BigEndian.PutUint32(b.bytes, uint32(value.Uint()))
}
b.bytes = b.bytes[4:]
case reflect.Uint64:
if len(b.bytes) < 8 {
return errWrongMarshalSize
}
if value.CanInterface() {
binary.BigEndian.PutUint64(b.bytes, value.Uint())
}
b.bytes = b.bytes[8:]
case reflect.Slice:
for i := 0; i < value.Len(); i++ {
if value.Index(i).CanInterface() {
if err := b.write(value.Index(i).Interface()); err != nil {
return err
}
} else {
b.bytes = b.bytes[value.Index(i).Type().Size():]
}
}
case reflect.Struct:
for i := 0; i < value.NumField(); i++ {
encoding := value.Type().Field(i).Tag.Get("encoding")
if encoding == omit {
continue
}
if value.Field(i).CanInterface() {
if err := b.write(value.Field(i).Interface()); err != nil {
return err
}
} else {
advance := int(value.Field(i).Type().Size())
if len(b.bytes) < advance {
return errWrongMarshalSize
}
b.bytes = b.bytes[advance:]
}
}
default:
return errBadStructMemberType
}
return nil
}
// Reads bytes from the buffer as necessary to populate
// the structure passed as a parameter. This function will
// modify the PacketBuffer.bytes slice to exclude those
// bytes that have already been read.
func (b *packetBuffer) read(v interface{}) error { //nolint:gocognit
ptr := reflect.ValueOf(v)
if ptr.Kind() != reflect.Ptr {
return errBadReadParameter
}
value := reflect.Indirect(ptr)
// If this is an interface, we need to make it concrete before using it
if value.Kind() == reflect.Interface {
value = reflect.ValueOf(value.Interface())
}
value = reflect.Indirect(value)
switch value.Kind() {
case reflect.Uint8:
if len(b.bytes) < 1 {
return errWrongMarshalSize
}
value.SetUint(uint64(b.bytes[0]))
b.bytes = b.bytes[1:]
case reflect.Uint16:
if len(b.bytes) < 2 {
return errWrongMarshalSize
}
value.SetUint(uint64(binary.BigEndian.Uint16(b.bytes)))
b.bytes = b.bytes[2:]
case reflect.Uint32:
if len(b.bytes) < 4 {
return errWrongMarshalSize
}
value.SetUint(uint64(binary.BigEndian.Uint32(b.bytes)))
b.bytes = b.bytes[4:]
case reflect.Uint64:
if len(b.bytes) < 8 {
return errWrongMarshalSize
}
value.SetUint(binary.BigEndian.Uint64(b.bytes))
b.bytes = b.bytes[8:]
case reflect.Slice:
// If we encounter a slice, we consume the rest of the data
// in the buffer and load it into the slice.
for len(b.bytes) > 0 {
newElementPtr := reflect.New(value.Type().Elem())
if err := b.read(newElementPtr.Interface()); err != nil {
return err
}
if value.CanSet() {
value.Set(reflect.Append(value, reflect.Indirect(newElementPtr)))
}
}
case reflect.Struct:
for i := 0; i < value.NumField(); i++ {
encoding := value.Type().Field(i).Tag.Get("encoding")
if encoding == omit {
continue
}
if value.Field(i).CanInterface() {
field := value.Field(i)
newFieldPtr := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // This is the only way to get a typed pointer to a structure's field
if err := b.read(newFieldPtr.Interface()); err != nil {
return err
}
} else {
advance := int(value.Field(i).Type().Size())
if len(b.bytes) < advance {
return errWrongMarshalSize
}
b.bytes = b.bytes[advance:]
}
}
default:
return errBadStructMemberType
}
return nil
}
// Consumes `size` bytes and returns them as an
// independent PacketBuffer
func (b *packetBuffer) split(size int) packetBuffer {
if size > len(b.bytes) {
size = len(b.bytes)
}
newBuffer := packetBuffer{bytes: b.bytes[:size]}
b.bytes = b.bytes[size:]
return newBuffer
}
// Returns the size that a structure will encode into.
// This fuction doesn't check that Write() will succeed,
// and may return unexpectedly large results for those
// structures that Write() will fail on
func wireSize(v interface{}) int {
value := reflect.ValueOf(v)
// Indirect is safe to call on non-pointers, and
// will simply return the same value in such cases
value = reflect.Indirect(value)
size := int(0)
switch value.Kind() {
case reflect.Slice:
for i := 0; i < value.Len(); i++ {
if value.Index(i).CanInterface() {
size += wireSize(value.Index(i).Interface())
} else {
size += int(value.Index(i).Type().Size())
}
}
case reflect.Struct:
for i := 0; i < value.NumField(); i++ {
encoding := value.Type().Field(i).Tag.Get("encoding")
if encoding == omit {
continue
}
if value.Field(i).CanInterface() {
size += wireSize(value.Field(i).Interface())
} else {
size += int(value.Field(i).Type().Size())
}
}
default:
size = int(value.Type().Size())
}
return size
}

View file

@ -0,0 +1,103 @@
package rtcp
import (
"fmt"
"reflect"
)
/*
Converts an RTCP Packet into a human-readable format. The Packets
themselves can control the presentation as follows:
- Fields of a type that have a String() method will be formatted
with that String method (which should not emit '\n' characters)
- Otherwise, fields with a tag containing a "fmt" string will use that
format when serializing the value. For example, to format an SSRC
value as base 16 insted of base 10:
type ExamplePacket struct {
LocalSSRC uint32 `fmt:"0x%X"`
RemotsSSRCs []uint32 `fmt:"%X"`
}
- If no fmt string is present, "%+v" is used by default
The intention of this stringify() function is to simplify creation
of String() methods on new packet types, as it provides a simple
baseline implementation that works well in the majority of cases.
*/
func stringify(p Packet) string {
value := reflect.Indirect(reflect.ValueOf(p))
return formatField(value.Type().String(), "", p, "")
}
func formatField(name string, format string, f interface{}, indent string) string { //nolint:gocognit
out := indent
value := reflect.ValueOf(f)
if !value.IsValid() {
return fmt.Sprintf("%s%s: <nil>\n", out, name)
}
isPacket := reflect.TypeOf(f).Implements(reflect.TypeOf((*Packet)(nil)).Elem())
// Resolve pointers to their underlying values
if value.Type().Kind() == reflect.Ptr && !value.IsNil() {
underlying := reflect.Indirect(value)
if underlying.IsValid() {
value = underlying
}
}
// If the field type has a custom String method, use that
// (unless we're a packet, since we want to avoid recursing
// back into this function if the Packet's String() method
// uses it)
if stringMethod := value.MethodByName("String"); !isPacket && stringMethod.IsValid() {
out += fmt.Sprintf("%s: %s\n", name, stringMethod.Call([]reflect.Value{}))
return out
}
switch value.Kind() {
case reflect.Struct:
out += fmt.Sprintf("%s:\n", name)
for i := 0; i < value.NumField(); i++ {
if value.Field(i).CanInterface() {
format = value.Type().Field(i).Tag.Get("fmt")
if format == "" {
format = "%+v"
}
out += formatField(value.Type().Field(i).Name, format, value.Field(i).Interface(), indent+"\t")
}
}
case reflect.Slice:
childKind := value.Type().Elem().Kind()
_, hasStringMethod := value.Type().Elem().MethodByName("String")
if hasStringMethod || childKind == reflect.Struct || childKind == reflect.Ptr || childKind == reflect.Interface || childKind == reflect.Slice {
out += fmt.Sprintf("%s:\n", name)
for i := 0; i < value.Len(); i++ {
childName := fmt.Sprint(i)
// Since interfaces can hold different types of things, we add the
// most specific type name to the name to make it clear what the
// subsequent fields represent.
if value.Index(i).Kind() == reflect.Interface {
childName += fmt.Sprintf(" (%s)", reflect.Indirect(reflect.ValueOf(value.Index(i).Interface())).Type())
}
if value.Index(i).CanInterface() {
out += formatField(childName, format, value.Index(i).Interface(), indent+"\t")
}
}
return out
}
// If we didn't take care of stringing the value already, we fall through to the
// generic case. This will print slices of basic types on a single line.
fallthrough
default:
if value.CanInterface() {
out += fmt.Sprintf("%s: "+format+"\n", name, value.Interface())
}
}
return out
}

View file

@ -14,8 +14,6 @@ type PictureLossIndication struct {
MediaSSRC uint32
}
var _ Packet = (*PictureLossIndication)(nil) // assert is a Packet
const (
pliLength = 2
)

View file

@ -14,7 +14,9 @@ type RapidResynchronizationRequest struct {
MediaSSRC uint32
}
var _ Packet = (*RapidResynchronizationRequest)(nil) // assert is a Packet
// RapidResynchronisationRequest is provided as RFC 6051 spells resynchronization with an s.
// We provide both names to be consistent with other RFCs which spell resynchronization with a z.
type RapidResynchronisationRequest = RapidResynchronizationRequest
const (
rrrLength = 2

View file

@ -6,8 +6,6 @@ import "fmt"
// a packet with an unknown type is encountered.
type RawPacket []byte
var _ Packet = (*RawPacket)(nil) // assert is a Packet
// Marshal encodes the packet in binary.
func (r RawPacket) Marshal() ([]byte, error) {
return r, nil

View file

@ -4,7 +4,7 @@ import (
"bytes"
"encoding/binary"
"fmt"
"math/bits"
"math"
)
// ReceiverEstimatedMaximumBitrate contains the receiver's estimated maximum bitrate.
@ -14,14 +14,12 @@ type ReceiverEstimatedMaximumBitrate struct {
SenderSSRC uint32
// Estimated maximum bitrate
Bitrate uint64
Bitrate float32
// SSRC entries which this packet applies to
SSRCs []uint32
}
var _ Packet = (*ReceiverEstimatedMaximumBitrate)(nil) // assert is a Packet
// Marshal serializes the packet and returns a byte slice.
func (p ReceiverEstimatedMaximumBitrate) Marshal() (buf []byte, err error) {
// Allocate a buffer of the exact output size.
@ -49,6 +47,7 @@ func (p ReceiverEstimatedMaximumBitrate) MarshalSize() (n int) {
// MarshalTo serializes the packet to the given byte slice.
func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error) {
const bitratemax = 0x3FFFFp+63
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -92,35 +91,32 @@ func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error
// Write the length of the ssrcs to follow at the end
buf[16] = byte(len(p.SSRCs))
// We can only encode 18 bits of information in the mantissa.
// The exponent lets us shift to the left up to 64 places (6-bits).
// We actually need a uint82 to encode the largest possible number,
// but uint64 should be good enough for 2.3 exabytes per second.
exp := 0
bitrate := p.Bitrate
// So we need to truncate the bitrate and use the exponent for the shift.
// bitrate = mantissa * (1 << exp)
// Calculate the total shift based on the leading number of zeroes.
// This will be negative if there is no shift required.
shift := uint(64 - bits.LeadingZeros64(p.Bitrate))
var mantissa uint
var exp uint
if shift <= 18 {
// Fit everything in the mantissa because we can.
mantissa = uint(p.Bitrate)
exp = 0
} else {
// We can only use 18 bits of precision, so truncate.
mantissa = uint(p.Bitrate >> (shift - 18))
exp = shift - 18
if bitrate >= bitratemax {
bitrate = bitratemax
}
if bitrate < 0 {
return 0, errInvalidBitrate
}
for bitrate >= (1 << 18) {
bitrate /= 2.0
exp++
}
if exp >= (1 << 6) {
return 0, errInvalidBitrate
}
mantissa := uint(math.Floor(float64(bitrate)))
// We can't quite use the binary package because
// a) it's a uint24 and b) the exponent is only 6-bits
// Just trust me; this is big-endian encoding.
buf[17] = byte((exp << 2) | (mantissa >> 16))
buf[17] = byte(exp<<2) | byte(mantissa>>16)
buf[18] = byte(mantissa >> 8)
buf[19] = byte(mantissa)
@ -136,6 +132,7 @@ func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error
// Unmarshal reads a REMB packet from the given byte slice.
func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) {
const mantissamax = 0x7FFFFF
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@ -221,20 +218,22 @@ func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) {
// Get the 6-bit exponent value.
exp := buf[17] >> 2
exp += 127 // bias for IEEE754
exp += 23 // IEEE754 biases the decimal to the left, abs-send-time biases it to the right
// The remaining 2-bits plus the next 16-bits are the mantissa.
mantissa := uint64(buf[17]&3)<<16 | uint64(buf[18])<<8 | uint64(buf[19])
mantissa := uint32(buf[17]&3)<<16 | uint32(buf[18])<<8 | uint32(buf[19])
if mantissa != 0 {
// ieee754 requires an implicit leading bit
for (mantissa & (mantissamax + 1)) == 0 {
exp--
mantissa *= 2
}
}
// bitrate = mantissa * 2^exp
if exp > 46 {
// NOTE: We intentionally truncate values so they fit in a uint64.
// Otherwise we would need a uint82.
// This is 2.3 exabytes per second, which should be good enough.
p.Bitrate = ^uint64(0)
} else {
p.Bitrate = mantissa << exp
}
p.Bitrate = math.Float32frombits((uint32(exp) << 23) | (mantissa & mantissamax))
// Clear any existing SSRCs
p.SSRCs = nil
@ -264,7 +263,7 @@ func (p *ReceiverEstimatedMaximumBitrate) String() string {
bitUnits := []string{"b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb"}
// Do some unit conversions because b/s is far too difficult to read.
bitrate := float64(p.Bitrate)
bitrate := p.Bitrate
powers := 0
// Keep dividing the bitrate until it's under 1000

View file

@ -19,8 +19,6 @@ type ReceiverReport struct {
ProfileExtensions []byte
}
var _ Packet = (*ReceiverReport)(nil) // assert is a Packet
const (
ssrcLength = 4
rrSSRCOffset = headerLength

View file

@ -1,15 +1,27 @@
{
"extends": [
"config:base"
"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"
]
}

View file

@ -0,0 +1,325 @@
package rtcp
import (
"encoding/binary"
"errors"
"fmt"
)
// https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P| FMT=11 | PT = 205 | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of RTCP packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of 1st RTP Stream |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | num_reports |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset | ... .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of nth RTP Stream |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | begin_seq | num_reports |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |R|ECN| Arrival time offset | ... |
// . .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Report Timestamp (32 bits) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
var (
errReportBlockLength = errors.New("feedback report blocks must be at least 8 bytes")
errIncorrectNumReports = errors.New("feedback report block contains less reports than num_reports")
errMetricBlockLength = errors.New("feedback report metric blocks must be exactly 2 bytes")
)
// ECN represents the two ECN bits
type ECN uint8
const (
//nolint:misspell
// ECNNonECT signals Non ECN-Capable Transport, Non-ECT
ECNNonECT ECN = iota // 00
//nolint:misspell
// ECNECT1 signals ECN Capable Transport, ECT(0)
ECNECT1 // 01
//nolint:misspell
// ECNECT0 signals ECN Capable Transport, ECT(1)
ECNECT0 // 10
// ECNCE signals ECN Congestion Encountered, CE
ECNCE // 11
)
const (
reportTimestampLength = 4
reportBlockOffset = 8
)
// CCFeedbackReport is a Congestion Control Feedback Report as defined in
// https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee
type CCFeedbackReport struct {
// SSRC of sender
SenderSSRC uint32
// Report Blocks
ReportBlocks []CCFeedbackReportBlock
// Basetime
ReportTimestamp uint32
}
// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (b CCFeedbackReport) DestinationSSRC() []uint32 {
ssrcs := make([]uint32, len(b.ReportBlocks))
for i, block := range b.ReportBlocks {
ssrcs[i] = block.MediaSSRC
}
return ssrcs
}
// Len returns the length of the report in bytes
func (b *CCFeedbackReport) Len() uint16 {
n := uint16(0)
for _, block := range b.ReportBlocks {
n += block.len()
}
return reportBlockOffset + n + reportTimestampLength
}
// Header returns the Header associated with this packet.
func (b *CCFeedbackReport) Header() Header {
return Header{
Padding: false,
Count: FormatCCFB,
Type: TypeTransportSpecificFeedback,
Length: b.Len()/4 - 1,
}
}
// Marshal encodes the Congestion Control Feedback Report in binary
func (b CCFeedbackReport) Marshal() ([]byte, error) {
header := b.Header()
headerBuf, err := header.Marshal()
if err != nil {
return nil, err
}
length := 4 * (header.Length + 1)
buf := make([]byte, length)
copy(buf[:headerLength], headerBuf)
binary.BigEndian.PutUint32(buf[headerLength:], b.SenderSSRC)
offset := uint16(reportBlockOffset)
for _, block := range b.ReportBlocks {
b, err := block.marshal()
if err != nil {
return nil, err
}
copy(buf[offset:], b)
offset += block.len()
}
binary.BigEndian.PutUint32(buf[offset:], b.ReportTimestamp)
return buf, nil
}
func (b CCFeedbackReport) String() string {
out := fmt.Sprintf("CCFB:\n\tHeader %v\n", b.Header())
out += fmt.Sprintf("CCFB:\n\tSender SSRC %d\n", b.SenderSSRC)
out += fmt.Sprintf("\tReport Timestamp %d\n", b.ReportTimestamp)
out += "\tFeedback Reports \n"
for _, report := range b.ReportBlocks {
out += fmt.Sprintf("%v ", report)
}
out += "\n"
return out
}
// Unmarshal decodes the Congestion Control Feedback Report from binary
func (b *CCFeedbackReport) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < headerLength+ssrcLength+reportTimestampLength {
return errPacketTooShort
}
var h Header
if err := h.Unmarshal(rawPacket); err != nil {
return err
}
if h.Type != TypeTransportSpecificFeedback {
return errWrongType
}
b.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
reportTimestampOffset := uint16(len(rawPacket) - reportTimestampLength)
b.ReportTimestamp = binary.BigEndian.Uint32(rawPacket[reportTimestampOffset:])
offset := uint16(reportBlockOffset)
b.ReportBlocks = []CCFeedbackReportBlock{}
for offset < reportTimestampOffset {
var block CCFeedbackReportBlock
if err := block.unmarshal(rawPacket[offset:]); err != nil {
return err
}
b.ReportBlocks = append(b.ReportBlocks, block)
offset += block.len()
}
return nil
}
const (
ssrcOffset = 0
beginSequenceOffset = 4
numReportsOffset = 6
reportsOffset = 8
maxMetricBlocks = 16384
)
// CCFeedbackReportBlock is a Feedback Report Block
type CCFeedbackReportBlock struct {
// SSRC of the RTP stream on which this block is reporting
MediaSSRC uint32
BeginSequence uint16
MetricBlocks []CCFeedbackMetricBlock
}
// len returns the length of the report block in bytes
func (b *CCFeedbackReportBlock) len() uint16 {
n := len(b.MetricBlocks)
if n%2 != 0 {
n++
}
return reportsOffset + 2*uint16(n)
}
func (b CCFeedbackReportBlock) String() string {
out := fmt.Sprintf("\tReport Block Media SSRC %d\n", b.MediaSSRC)
out += fmt.Sprintf("\tReport Begin Sequence Nr %d\n", b.BeginSequence)
out += fmt.Sprintf("\tReport length %d\n\t", len(b.MetricBlocks))
for i, block := range b.MetricBlocks {
out += fmt.Sprintf("{nr: %d, rx: %v, ts: %v} ", b.BeginSequence+uint16(i), block.Received, block.ArrivalTimeOffset)
}
out += "\n"
return out
}
// marshal encodes the Congestion Control Feedback Report Block in binary
func (b CCFeedbackReportBlock) marshal() ([]byte, error) {
if len(b.MetricBlocks) > maxMetricBlocks {
return nil, errTooManyReports
}
buf := make([]byte, b.len())
binary.BigEndian.PutUint32(buf[ssrcOffset:], b.MediaSSRC)
binary.BigEndian.PutUint16(buf[beginSequenceOffset:], b.BeginSequence)
length := uint16(len(b.MetricBlocks))
if length > 0 {
length--
}
binary.BigEndian.PutUint16(buf[numReportsOffset:], length)
for i, block := range b.MetricBlocks {
b, err := block.marshal()
if err != nil {
return nil, err
}
copy(buf[reportsOffset+i*2:], b)
}
return buf, nil
}
// Unmarshal decodes the Congestion Control Feedback Report Block from binary
func (b *CCFeedbackReportBlock) unmarshal(rawPacket []byte) error {
if len(rawPacket) < reportsOffset {
return errReportBlockLength
}
b.MediaSSRC = binary.BigEndian.Uint32(rawPacket[:beginSequenceOffset])
b.BeginSequence = binary.BigEndian.Uint16(rawPacket[beginSequenceOffset:numReportsOffset])
numReportsField := binary.BigEndian.Uint16(rawPacket[numReportsOffset:])
if numReportsField == 0 {
return nil
}
endSequence := b.BeginSequence + numReportsField
numReports := endSequence - b.BeginSequence + 1
if len(rawPacket) < int(reportsOffset+numReports*2) {
return errIncorrectNumReports
}
b.MetricBlocks = make([]CCFeedbackMetricBlock, numReports)
for i := uint16(0); i < numReports; i++ {
var mb CCFeedbackMetricBlock
offset := reportsOffset + 2*i
if err := mb.unmarshal(rawPacket[offset : offset+2]); err != nil {
return err
}
b.MetricBlocks[i] = mb
}
return nil
}
const (
metricBlockLength = 2
)
// CCFeedbackMetricBlock is a Feedback Metric Block
type CCFeedbackMetricBlock struct {
Received bool
ECN ECN
// Offset in 1/1024 seconds before Report Timestamp
ArrivalTimeOffset uint16
}
// Marshal encodes the Congestion Control Feedback Metric Block in binary
func (b CCFeedbackMetricBlock) marshal() ([]byte, error) {
buf := make([]byte, 2)
r := uint16(0)
if b.Received {
r = 1
}
dst, err := setNBitsOfUint16(0, 1, 0, r)
if err != nil {
return nil, err
}
dst, err = setNBitsOfUint16(dst, 2, 1, uint16(b.ECN))
if err != nil {
return nil, err
}
dst, err = setNBitsOfUint16(dst, 13, 3, b.ArrivalTimeOffset)
if err != nil {
return nil, err
}
binary.BigEndian.PutUint16(buf, dst)
return buf, nil
}
// Unmarshal decodes the Congestion Control Feedback Metric Block from binary
func (b *CCFeedbackMetricBlock) unmarshal(rawPacket []byte) error {
if len(rawPacket) != metricBlockLength {
return errMetricBlockLength
}
b.Received = rawPacket[0]&0x80 != 0
if !b.Received {
b.ECN = ECNNonECT
b.ArrivalTimeOffset = 0
return nil
}
b.ECN = ECN(rawPacket[0] >> 5 & 0x03)
b.ArrivalTimeOffset = binary.BigEndian.Uint16(rawPacket) & 0x1FFF
return nil
}

View file

@ -39,8 +39,6 @@ type SenderReport struct {
ProfileExtensions []byte
}
var _ Packet = (*SenderReport)(nil) // assert is a Packet
const (
srHeaderLength = 24
srSSRCOffset = 0

View file

@ -30,8 +30,6 @@ type SliceLossIndication struct {
SLI []SLIEntry
}
var _ Packet = (*SliceLossIndication)(nil) // assert is a Packet
const (
sliLength = 2
sliOffset = 8

View file

@ -61,7 +61,18 @@ type SourceDescription struct {
Chunks []SourceDescriptionChunk
}
var _ Packet = (*SourceDescription)(nil) // assert is a Packet
// NewCNAMESourceDescription creates a new SourceDescription with a single CNAME item.
func NewCNAMESourceDescription(ssrc uint32, cname string) *SourceDescription {
return &SourceDescription{
Chunks: []SourceDescriptionChunk{{
Source: ssrc,
Items: []SourceDescriptionItem{{
Type: SDESCNAME,
Text: cname,
}},
}},
}
}
// Marshal encodes the SourceDescription in binary
func (s SourceDescription) Marshal() ([]byte, error) {
@ -244,16 +255,16 @@ func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
}
func (s SourceDescriptionChunk) len() int {
len := sdesSourceLen
chunkLen := sdesSourceLen
for _, it := range s.Items {
len += it.len()
chunkLen += it.len()
}
len += sdesTypeLen // for terminating null octet
chunkLen += sdesTypeLen // for terminating null octet
// align to 32-bit boundary
len += getPadding(len)
chunkLen += getPadding(chunkLen)
return len
return chunkLen
}
// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.

View file

@ -75,8 +75,6 @@ func numOfBitsOfSymbolSize() map[uint16]uint16 {
}
}
var _ Packet = (*TransportLayerCC)(nil) // assert is a Packet
var (
errPacketStatusChunkLength = errors.New("packet status chunk must be 2 bytes")
errDeltaExceedLimit = errors.New("delta exceed limit")
@ -351,10 +349,7 @@ type TransportLayerCC struct {
func (t *TransportLayerCC) packetLen() uint16 {
n := uint16(headerLength + packetChunkOffset + len(t.PacketChunks)*2)
for _, d := range t.RecvDeltas {
delta := d.Delta / TypeTCCDeltaScaleFactor
// small delta
if delta >= 0 && delta <= math.MaxUint8 {
if d.Type == TypeTCCPacketReceivedSmallDelta {
n++
} else {
n += 2
@ -452,7 +447,7 @@ func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
// header's length + payload's length
totalLength := 4 * (t.Header.Length + 1)
if totalLength <= headerLength+packetChunkOffset {
if totalLength < headerLength+packetChunkOffset {
return errPacketTooShort
}

View file

@ -33,8 +33,6 @@ type TransportLayerNack struct {
Nacks []NackPair
}
var _ Packet = (*TransportLayerNack)(nil) // assert is a Packet
// NackPairsFromSequenceNumbers generates a slice of NackPair from a list of SequenceNumbers
// This handles generating the proper values for PacketID/LostPackets
func NackPairsFromSequenceNumbers(sequenceNumbers []uint16) (pairs []NackPair) {

View file

@ -1,11 +1,11 @@
package rtcp
// getPadding Returns the padding required to make the length a multiple of 4
func getPadding(len int) int {
if len%4 == 0 {
func getPadding(packetLen int) int {
if packetLen%4 == 0 {
return 0
}
return 4 - (len % 4)
return 4 - (packetLen % 4)
}
// setNBitsOfUint16 will truncate the value to size, left-shift to startIndex position and set