diff options
author | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 01:46:27 +0100 |
---|---|---|
committer | kali kaneko (leap communications) <kali@leap.se> | 2021-11-29 18:14:16 +0100 |
commit | 18f52af5be3a9a0c73811706108f790d65ee9c67 (patch) | |
tree | e13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com/pion/rtcp | |
parent | ebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff) |
[pkg] update vendor
Diffstat (limited to 'vendor/github.com/pion/rtcp')
28 files changed, 3327 insertions, 0 deletions
diff --git a/vendor/github.com/pion/rtcp/.gitignore b/vendor/github.com/pion/rtcp/.gitignore new file mode 100644 index 0000000..83db74b --- /dev/null +++ b/vendor/github.com/pion/rtcp/.gitignore @@ -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 diff --git a/vendor/github.com/pion/rtcp/.golangci.yml b/vendor/github.com/pion/rtcp/.golangci.yml new file mode 100644 index 0000000..d6162c9 --- /dev/null +++ b/vendor/github.com/pion/rtcp/.golangci.yml @@ -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 diff --git a/vendor/github.com/pion/rtcp/LICENSE b/vendor/github.com/pion/rtcp/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/vendor/github.com/pion/rtcp/LICENSE @@ -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. diff --git a/vendor/github.com/pion/rtcp/README.md b/vendor/github.com/pion/rtcp/README.md new file mode 100644 index 0000000..a054dae --- /dev/null +++ b/vendor/github.com/pion/rtcp/README.md @@ -0,0 +1,49 @@ +<h1 align="center"> + <br> + Pion RTCP + <br> +</h1> +<h4 align="center">A Go implementation of RTCP</h4> +<p align="center"> + <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-rtcp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion RTCP"></a> + <a href="https://sourcegraph.com/github.com/pion/rtcp?badge"><img src="https://sourcegraph.com/github.com/pion/rtcp/-/badge.svg" alt="Sourcegraph Widget"></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://travis-ci.org/pion/rtcp"><img src="https://travis-ci.org/pion/rtcp.svg?branch=master" alt="Build Status"></a> + <a href="https://pkg.go.dev/github.com/pion/rtcp"><img src="https://godoc.org/github.com/pion/rtcp?status.svg" alt="GoDoc"></a> + <a href="https://codecov.io/gh/pion/rtcp"><img src="https://codecov.io/gh/pion/rtcp/branch/master/graph/badge.svg" alt="Coverage Status"></a> + <a href="https://goreportcard.com/report/github.com/pion/rtcp"><img src="https://goreportcard.com/badge/github.com/pion/rtcp" 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> + +See [DESIGN.md](DESIGN.md) for an overview of features and future goals. + +### Roadmap +The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. + +### Community +Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). + +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: + +* [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 diff --git a/vendor/github.com/pion/rtcp/codecov.yml b/vendor/github.com/pion/rtcp/codecov.yml new file mode 100644 index 0000000..085200a --- /dev/null +++ b/vendor/github.com/pion/rtcp/codecov.yml @@ -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/**/*" diff --git a/vendor/github.com/pion/rtcp/compound_packet.go b/vendor/github.com/pion/rtcp/compound_packet.go new file mode 100644 index 0000000..2c74279 --- /dev/null +++ b/vendor/github.com/pion/rtcp/compound_packet.go @@ -0,0 +1,136 @@ +package rtcp + +// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with +// the underlying protocol (for example UDP). +// +// To maximize the resolution of receiption statistics, the first Packet in a CompoundPacket +// must always be either a SenderReport or a ReceiverReport. This is true even if no data +// has been sent or received, in which case an empty ReceiverReport must be sent, and even +// if the only other RTCP packet in the compound packet is a Goodbye. +// +// Next, a SourceDescription containing a CNAME item must be included in each CompoundPacket +// to identify the source and to begin associating media for purposes such as lip-sync. +// +// 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 { + return errEmptyCompound + } + + // SenderReport and ReceiverReport are the only types that + // are allowed to be the first packet in a compound datagram + switch c[0].(type) { + case *SenderReport, *ReceiverReport: + // ok + default: + return errBadFirstPacket + } + + for _, pkt := range c[1:] { + switch p := pkt.(type) { + // If the number of RecetpionReports exceeds 31 additional ReceiverReports + // can be included here. + case *ReceiverReport: + continue + + // A SourceDescription containing a CNAME must be included in every + // CompoundPacket. + case *SourceDescription: + var hasCNAME bool + for _, c := range p.Chunks { + for _, it := range c.Items { + if it.Type == SDESCNAME { + hasCNAME = true + } + } + } + + if !hasCNAME { + return errMissingCNAME + } + + return nil + + // Other packets are not permitted before the CNAME + default: + return errPacketBeforeCNAME + } + } + + // CNAME never reached + return errMissingCNAME +} + +// CNAME returns the CNAME that *must* be present in every CompoundPacket +func (c CompoundPacket) CNAME() (string, error) { + var err error + + if len(c) < 1 { + return "", errEmptyCompound + } + + for _, pkt := range c[1:] { + sdes, ok := pkt.(*SourceDescription) + if ok { + for _, c := range sdes.Chunks { + for _, it := range c.Items { + if it.Type == SDESCNAME { + return it.Text, err + } + } + } + } else { + _, ok := pkt.(*ReceiverReport) + if !ok { + err = errPacketBeforeCNAME + } + } + } + return "", errMissingCNAME +} + +// Marshal encodes the CompoundPacket as binary. +func (c CompoundPacket) Marshal() ([]byte, error) { + if err := c.Validate(); err != nil { + return nil, err + } + + p := []Packet(c) + return Marshal(p) +} + +// Unmarshal decodes a CompoundPacket from binary. +func (c *CompoundPacket) Unmarshal(rawData []byte) error { + out := make(CompoundPacket, 0) + for len(rawData) != 0 { + p, processed, err := unmarshal(rawData) + if err != nil { + return err + } + + out = append(out, p) + rawData = rawData[processed:] + } + *c = out + + if err := c.Validate(); err != nil { + return err + } + + return nil +} + +// DestinationSSRC returns the synchronization sources associated with this +// CompoundPacket's reception report. +func (c CompoundPacket) DestinationSSRC() []uint32 { + if len(c) == 0 { + return nil + } + + return c[0].DestinationSSRC() +} diff --git a/vendor/github.com/pion/rtcp/doc.go b/vendor/github.com/pion/rtcp/doc.go new file mode 100644 index 0000000..fa948df --- /dev/null +++ b/vendor/github.com/pion/rtcp/doc.go @@ -0,0 +1,39 @@ +/* +Package rtcp implements encoding and decoding of RTCP packets according to RFCs 3550 and 5506. + +RTCP is a sister protocol of the Real-time Transport Protocol (RTP). Its basic functionality +and packet structure is defined in RFC 3550. RTCP provides out-of-band statistics and control +information for an RTP session. It partners with RTP in the delivery and packaging of multimedia data, +but does not transport any media data itself. + +The primary function of RTCP is to provide feedback on the quality of service (QoS) +in media distribution by periodically sending statistics information such as transmitted octet +and packet counts, packet loss, packet delay variation, and round-trip delay time to participants +in a streaming multimedia session. An application may use this information to control quality of +service parameters, perhaps by limiting flow, or using a different codec. + +Decoding RTCP packets: + + pkt, err := rtcp.Unmarshal(rtcpData) + // ... + + switch p := pkt.(type) { + case *rtcp.CompoundPacket: + ... + case *rtcp.PictureLossIndication: + ... + default: + ... + } + +Encoding RTCP packets: + + pkt := &rtcp.PictureLossIndication{ + SenderSSRC: senderSSRC, + MediaSSRC: mediaSSRC + } + pliData, err := pkt.Marshal() + // ... + +*/ +package rtcp diff --git a/vendor/github.com/pion/rtcp/errors.go b/vendor/github.com/pion/rtcp/errors.go new file mode 100644 index 0000000..d1b00c5 --- /dev/null +++ b/vendor/github.com/pion/rtcp/errors.go @@ -0,0 +1,30 @@ +package rtcp + +import "errors" + +var ( + errWrongMarshalSize = errors.New("rtcp: wrong marshal size") + errInvalidTotalLost = errors.New("rtcp: invalid total lost count") + errInvalidHeader = errors.New("rtcp: invalid header") + errEmptyCompound = errors.New("rtcp: empty compound packet") + errBadFirstPacket = errors.New("rtcp: first packet in compound must be SR or RR") + errMissingCNAME = errors.New("rtcp: compound missing SourceDescription with CNAME") + errPacketBeforeCNAME = errors.New("rtcp: feedback packet seen before CNAME") + errTooManyReports = errors.New("rtcp: too many reports") + errTooManyChunks = errors.New("rtcp: too many chunks") + errTooManySources = errors.New("rtcp: too many sources") + errPacketTooShort = errors.New("rtcp: packet too short") + errWrongType = errors.New("rtcp: wrong packet type") + errSDESTextTooLong = errors.New("rtcp: sdes must be < 255 octets long") + errSDESMissingType = errors.New("rtcp: sdes item missing type") + errReasonTooLong = errors.New("rtcp: reason must be < 255 octets long") + errBadVersion = errors.New("rtcp: invalid packet version") + errWrongPadding = errors.New("rtcp: invalid padding value") + errWrongFeedbackType = errors.New("rtcp: wrong feedback message type") + errWrongPayloadType = errors.New("rtcp: wrong payload type") + errHeaderTooSmall = errors.New("rtcp: header length is too small") + errSSRCMustBeZero = errors.New("rtcp: media SSRC must be 0") + errMissingREMBidentifier = errors.New("missing REMB identifier") + errSSRCNumAndLengthMismatch = errors.New("SSRC num and length do not match") + errInvalidSizeOrStartIndex = errors.New("invalid size or startIndex") +) diff --git a/vendor/github.com/pion/rtcp/full_intra_request.go b/vendor/github.com/pion/rtcp/full_intra_request.go new file mode 100644 index 0000000..74ca928 --- /dev/null +++ b/vendor/github.com/pion/rtcp/full_intra_request.go @@ -0,0 +1,107 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// A FIREntry is a (SSRC, seqno) pair, as carried by FullIntraRequest. +type FIREntry struct { + SSRC uint32 + SequenceNumber uint8 +} + +// The FullIntraRequest packet is used to reliably request an Intra frame +// in a video stream. See RFC 5104 Section 3.5.1. This is not for loss +// recovery, which should use PictureLossIndication (PLI) instead. +type FullIntraRequest struct { + SenderSSRC uint32 + MediaSSRC uint32 + + FIR []FIREntry +} + +const ( + firOffset = 8 +) + +var _ Packet = (*FullIntraRequest)(nil) + +// Marshal encodes the FullIntraRequest +func (p FullIntraRequest) Marshal() ([]byte, error) { + rawPacket := make([]byte, firOffset+(len(p.FIR)*8)) + binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC) + binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC) + for i, fir := range p.FIR { + binary.BigEndian.PutUint32(rawPacket[firOffset+8*i:], fir.SSRC) + rawPacket[firOffset+8*i+4] = fir.SequenceNumber + } + h := p.Header() + hData, err := h.Marshal() + if err != nil { + return nil, err + } + + return append(hData, rawPacket...), nil +} + +// Unmarshal decodes the TransportLayerNack +func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < (headerLength + ssrcLength) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if len(rawPacket) < (headerLength + int(4*h.Length)) { + return errPacketTooShort + } + + if h.Type != TypePayloadSpecificFeedback || h.Count != FormatFIR { + return errWrongType + } + + p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + for i := headerLength + firOffset; i < (headerLength + int(h.Length*4)); i += 8 { + p.FIR = append(p.FIR, FIREntry{ + binary.BigEndian.Uint32(rawPacket[i:]), + rawPacket[i+4], + }) + } + return nil +} + +// Header returns the Header associated with this packet. +func (p *FullIntraRequest) Header() Header { + return Header{ + Count: FormatFIR, + Type: TypePayloadSpecificFeedback, + Length: uint16((p.len() / 4) - 1), + } +} + +func (p *FullIntraRequest) len() int { + return headerLength + firOffset + len(p.FIR)*8 +} + +func (p *FullIntraRequest) String() string { + out := fmt.Sprintf("FullIntraRequest %x %x", + p.SenderSSRC, p.MediaSSRC) + for _, e := range p.FIR { + out += fmt.Sprintf(" (%x %v)", e.SSRC, e.SequenceNumber) + } + return out +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *FullIntraRequest) DestinationSSRC() []uint32 { + ssrcs := make([]uint32, 0, len(p.FIR)) + for _, entry := range p.FIR { + ssrcs = append(ssrcs, entry.SSRC) + } + return ssrcs +} diff --git a/vendor/github.com/pion/rtcp/fuzz.go b/vendor/github.com/pion/rtcp/fuzz.go new file mode 100644 index 0000000..2ea4fb1 --- /dev/null +++ b/vendor/github.com/pion/rtcp/fuzz.go @@ -0,0 +1,51 @@ +// +build gofuzz + +package rtcp + +import ( + "bytes" + "io" +) + +// Fuzz implements a randomized fuzz test of the rtcp +// parser using go-fuzz. +// +// To run the fuzzer, first download go-fuzz: +// `go get github.com/dvyukov/go-fuzz/...` +// +// Then build the testing package: +// `go-fuzz-build github.com/pion/webrtc` +// +// And run the fuzzer on the corpus: +// ``` +// mkdir workdir +// +// # optionally add a starter corpus of valid rtcp packets. +// # the corpus should be as compact and diverse as possible. +// cp -r ~/my-rtcp-packets workdir/corpus +// +// go-fuzz -bin=ase-fuzz.zip -workdir=workdir +// ```` +func Fuzz(data []byte) int { + r := NewReader(bytes.NewReader(data)) + for { + _, data, err := r.ReadPacket() + if err == io.EOF { + break + } + if err != nil { + return 0 + } + + packet, err := Unmarshal(data) + if err != nil { + return 0 + } + + if _, err := packet.Marshal(); err != nil { + return 0 + } + } + + return 1 +} diff --git a/vendor/github.com/pion/rtcp/go.mod b/vendor/github.com/pion/rtcp/go.mod new file mode 100644 index 0000000..28b6e9d --- /dev/null +++ b/vendor/github.com/pion/rtcp/go.mod @@ -0,0 +1,5 @@ +module github.com/pion/rtcp + +go 1.13 + +require github.com/stretchr/testify v1.6.1 diff --git a/vendor/github.com/pion/rtcp/go.sum b/vendor/github.com/pion/rtcp/go.sum new file mode 100644 index 0000000..afe7890 --- /dev/null +++ b/vendor/github.com/pion/rtcp/go.sum @@ -0,0 +1,11 @@ +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= diff --git a/vendor/github.com/pion/rtcp/goodbye.go b/vendor/github.com/pion/rtcp/goodbye.go new file mode 100644 index 0000000..b8718b3 --- /dev/null +++ b/vendor/github.com/pion/rtcp/goodbye.go @@ -0,0 +1,146 @@ +package rtcp + +import ( + "encoding/binary" +) + +// The Goodbye packet indicates that one or more sources are no longer active. +type Goodbye struct { + // The SSRC/CSRC identifiers that are no longer active + Sources []uint32 + // Optional text indicating the reason for leaving, e.g., "camera malfunction" or "RTP loop detected" + Reason string +} + +var _ Packet = (*Goodbye)(nil) // assert is a Packet + +// Marshal encodes the Goodbye packet in binary +func (g Goodbye) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P| SC | PT=BYE=203 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC/CSRC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * (opt) | length | reason for leaving ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + rawPacket := make([]byte, g.len()) + packetBody := rawPacket[headerLength:] + + if len(g.Sources) > countMax { + return nil, errTooManySources + } + + for i, s := range g.Sources { + binary.BigEndian.PutUint32(packetBody[i*ssrcLength:], s) + } + + if g.Reason != "" { + reason := []byte(g.Reason) + + if len(reason) > sdesMaxOctetCount { + return nil, errReasonTooLong + } + + reasonOffset := len(g.Sources) * ssrcLength + packetBody[reasonOffset] = uint8(len(reason)) + copy(packetBody[reasonOffset+1:], reason) + } + + hData, err := g.Header().Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the Goodbye packet from binary +func (g *Goodbye) Unmarshal(rawPacket []byte) error { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P| SC | PT=BYE=203 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC/CSRC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * (opt) | length | reason for leaving ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + var header Header + if err := header.Unmarshal(rawPacket); err != nil { + return err + } + + if header.Type != TypeGoodbye { + return errWrongType + } + + if getPadding(len(rawPacket)) != 0 { + return errPacketTooShort + } + + g.Sources = make([]uint32, header.Count) + + reasonOffset := int(headerLength + header.Count*ssrcLength) + if reasonOffset > len(rawPacket) { + return errPacketTooShort + } + + for i := 0; i < int(header.Count); i++ { + offset := headerLength + i*ssrcLength + + g.Sources[i] = binary.BigEndian.Uint32(rawPacket[offset:]) + } + + if reasonOffset < len(rawPacket) { + reasonLen := int(rawPacket[reasonOffset]) + reasonEnd := reasonOffset + 1 + reasonLen + + if reasonEnd > len(rawPacket) { + return errPacketTooShort + } + + g.Reason = string(rawPacket[reasonOffset+1 : reasonEnd]) + } + + return nil +} + +// Header returns the Header associated with this packet. +func (g *Goodbye) Header() Header { + return Header{ + Padding: false, + Count: uint8(len(g.Sources)), + Type: TypeGoodbye, + Length: uint16((g.len() / 4) - 1), + } +} + +func (g *Goodbye) len() int { + srcsLength := len(g.Sources) * ssrcLength + reasonLength := len(g.Reason) + 1 + + l := headerLength + srcsLength + reasonLength + + // align to 32-bit boundary + return l + getPadding(l) +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (g *Goodbye) DestinationSSRC() []uint32 { + out := make([]uint32, len(g.Sources)) + copy(out, g.Sources) + return out +} diff --git a/vendor/github.com/pion/rtcp/header.go b/vendor/github.com/pion/rtcp/header.go new file mode 100644 index 0000000..055ca18 --- /dev/null +++ b/vendor/github.com/pion/rtcp/header.go @@ -0,0 +1,140 @@ +package rtcp + +import ( + "encoding/binary" +) + +// PacketType specifies the type of an RTCP packet +type PacketType uint8 + +// RTCP packet types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4 +const ( + TypeSenderReport PacketType = 200 // RFC 3550, 6.4.1 + TypeReceiverReport PacketType = 201 // RFC 3550, 6.4.2 + TypeSourceDescription PacketType = 202 // RFC 3550, 6.5 + TypeGoodbye PacketType = 203 // RFC 3550, 6.6 + TypeApplicationDefined PacketType = 204 // RFC 3550, 6.7 (unimplemented) + TypeTransportSpecificFeedback PacketType = 205 // RFC 4585, 6051 + TypePayloadSpecificFeedback PacketType = 206 // RFC 4585, 6.3 + +) + +// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here +const ( + FormatSLI uint8 = 2 + FormatPLI uint8 = 1 + FormatFIR uint8 = 4 + FormatTLN uint8 = 1 + FormatRRR uint8 = 5 + FormatREMB uint8 = 15 + + //https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5 + FormatTCC uint8 = 15 +) + +func (p PacketType) String() string { + switch p { + case TypeSenderReport: + return "SR" + case TypeReceiverReport: + return "RR" + case TypeSourceDescription: + return "SDES" + case TypeGoodbye: + return "BYE" + case TypeApplicationDefined: + return "APP" + case TypeTransportSpecificFeedback: + return "TSFB" + case TypePayloadSpecificFeedback: + return "PSFB" + default: + return string(p) + } +} + +const rtpVersion = 2 + +// A Header is the common header shared by all RTCP packets +type Header struct { + // If the padding bit is set, this individual RTCP packet contains + // some additional padding octets at the end which are not part of + // the control information but are included in the length field. + Padding bool + // The number of reception reports, sources contained or FMT in this packet (depending on the Type) + Count uint8 + // The RTCP packet type for this packet + Type PacketType + // The length of this RTCP packet in 32-bit words minus one, + // including the header and any padding. + Length uint16 +} + +const ( + headerLength = 4 + versionShift = 6 + versionMask = 0x3 + paddingShift = 5 + paddingMask = 0x1 + countShift = 0 + countMask = 0x1f + countMax = (1 << 5) - 1 +) + +// Marshal encodes the Header in binary +func (h Header) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |V=2|P| RC | PT=SR=200 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + rawPacket := make([]byte, headerLength) + + rawPacket[0] |= rtpVersion << versionShift + + if h.Padding { + rawPacket[0] |= 1 << paddingShift + } + + if h.Count > 31 { + return nil, errInvalidHeader + } + rawPacket[0] |= h.Count << countShift + + rawPacket[1] = uint8(h.Type) + + binary.BigEndian.PutUint16(rawPacket[2:], h.Length) + + return rawPacket, nil +} + +// Unmarshal decodes the Header from binary +func (h *Header) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < headerLength { + return errPacketTooShort + } + + /* + * 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| RC | PT | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + version := rawPacket[0] >> versionShift & versionMask + if version != rtpVersion { + return errBadVersion + } + + h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0 + h.Count = rawPacket[0] >> countShift & countMask + + h.Type = PacketType(rawPacket[1]) + + h.Length = binary.BigEndian.Uint16(rawPacket[2:]) + + return nil +} diff --git a/vendor/github.com/pion/rtcp/packet.go b/vendor/github.com/pion/rtcp/packet.go new file mode 100644 index 0000000..de08003 --- /dev/null +++ b/vendor/github.com/pion/rtcp/packet.go @@ -0,0 +1,115 @@ +package rtcp + +// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session +type Packet interface { + // DestinationSSRC returns an array of SSRC values that this packet refers to. + DestinationSSRC() []uint32 + + Marshal() ([]byte, error) + Unmarshal(rawPacket []byte) error +} + +// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and +// returns the unmarshaled packets it contains. +// +// If this is a reduced-size RTCP packet a feedback packet (Goodbye, SliceLossIndication, etc) +// will be returned. Otherwise, the underlying type of the returned packet will be +// CompoundPacket. +func Unmarshal(rawData []byte) ([]Packet, error) { + var packets []Packet + for len(rawData) != 0 { + p, processed, err := unmarshal(rawData) + if err != nil { + return nil, err + } + + packets = append(packets, p) + rawData = rawData[processed:] + } + + switch len(packets) { + // Empty packet + case 0: + return nil, errInvalidHeader + // Multiple Packets + default: + return packets, nil + } +} + +// Marshal takes an array of Packets and serializes them to a single buffer +func Marshal(packets []Packet) ([]byte, error) { + out := make([]byte, 0) + for _, p := range packets { + data, err := p.Marshal() + if err != nil { + return nil, err + } + out = append(out, data...) + } + return out, nil +} + +// unmarshal is a factory which pulls the first RTCP packet from a bytestream, +// and returns it's parsed representation, and the amount of data that was processed. +func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) { + var h Header + + err = h.Unmarshal(rawData) + if err != nil { + return nil, 0, err + } + + bytesprocessed = int(h.Length+1) * 4 + if bytesprocessed > len(rawData) { + return nil, 0, errPacketTooShort + } + inPacket := rawData[:bytesprocessed] + + switch h.Type { + case TypeSenderReport: + packet = new(SenderReport) + + case TypeReceiverReport: + packet = new(ReceiverReport) + + case TypeSourceDescription: + packet = new(SourceDescription) + + case TypeGoodbye: + packet = new(Goodbye) + + case TypeTransportSpecificFeedback: + switch h.Count { + case FormatTLN: + packet = new(TransportLayerNack) + case FormatRRR: + packet = new(RapidResynchronizationRequest) + case FormatTCC: + packet = new(TransportLayerCC) + default: + packet = new(RawPacket) + } + + case TypePayloadSpecificFeedback: + switch h.Count { + case FormatPLI: + packet = new(PictureLossIndication) + case FormatSLI: + packet = new(SliceLossIndication) + case FormatREMB: + packet = new(ReceiverEstimatedMaximumBitrate) + case FormatFIR: + packet = new(FullIntraRequest) + default: + packet = new(RawPacket) + } + + default: + packet = new(RawPacket) + } + + err = packet.Unmarshal(inPacket) + + return packet, bytesprocessed, err +} diff --git a/vendor/github.com/pion/rtcp/picture_loss_indication.go b/vendor/github.com/pion/rtcp/picture_loss_indication.go new file mode 100644 index 0000000..7216ecd --- /dev/null +++ b/vendor/github.com/pion/rtcp/picture_loss_indication.go @@ -0,0 +1,91 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// The PictureLossIndication packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures +type PictureLossIndication struct { + // SSRC of sender + SenderSSRC uint32 + + // SSRC where the loss was experienced + MediaSSRC uint32 +} + +var _ Packet = (*PictureLossIndication)(nil) // assert is a Packet + +const ( + pliLength = 2 +) + +// Marshal encodes the PictureLossIndication in binary +func (p PictureLossIndication) Marshal() ([]byte, error) { + /* + * PLI does not require parameters. Therefore, the length field MUST be + * 2, and there MUST NOT be any Feedback Control Information. + * + * The semantics of this FB message is independent of the payload type. + */ + rawPacket := make([]byte, p.len()) + packetBody := rawPacket[headerLength:] + + binary.BigEndian.PutUint32(packetBody, p.SenderSSRC) + binary.BigEndian.PutUint32(packetBody[4:], p.MediaSSRC) + + h := Header{ + Count: FormatPLI, + Type: TypePayloadSpecificFeedback, + Length: pliLength, + } + hData, err := h.Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the PictureLossIndication from binary +func (p *PictureLossIndication) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < (headerLength + (ssrcLength * 2)) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if h.Type != TypePayloadSpecificFeedback || h.Count != FormatPLI { + return errWrongType + } + + p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + return nil +} + +// Header returns the Header associated with this packet. +func (p *PictureLossIndication) Header() Header { + return Header{ + Count: FormatPLI, + Type: TypePayloadSpecificFeedback, + Length: pliLength, + } +} + +func (p *PictureLossIndication) len() int { + return headerLength + ssrcLength*2 +} + +func (p *PictureLossIndication) String() string { + return fmt.Sprintf("PictureLossIndication %x %x", p.SenderSSRC, p.MediaSSRC) +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *PictureLossIndication) DestinationSSRC() []uint32 { + return []uint32{p.MediaSSRC} +} diff --git a/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go new file mode 100644 index 0000000..5d27055 --- /dev/null +++ b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go @@ -0,0 +1,88 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// The RapidResynchronizationRequest packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures +type RapidResynchronizationRequest struct { + // SSRC of sender + SenderSSRC uint32 + + // SSRC of the media source + MediaSSRC uint32 +} + +var _ Packet = (*RapidResynchronizationRequest)(nil) // assert is a Packet + +const ( + rrrLength = 2 + rrrHeaderLength = ssrcLength * 2 + rrrMediaOffset = 4 +) + +// Marshal encodes the RapidResynchronizationRequest in binary +func (p RapidResynchronizationRequest) Marshal() ([]byte, error) { + /* + * RRR does not require parameters. Therefore, the length field MUST be + * 2, and there MUST NOT be any Feedback Control Information. + * + * The semantics of this FB message is independent of the payload type. + */ + rawPacket := make([]byte, p.len()) + packetBody := rawPacket[headerLength:] + + binary.BigEndian.PutUint32(packetBody, p.SenderSSRC) + binary.BigEndian.PutUint32(packetBody[rrrMediaOffset:], p.MediaSSRC) + + hData, err := p.Header().Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the RapidResynchronizationRequest from binary +func (p *RapidResynchronizationRequest) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < (headerLength + (ssrcLength * 2)) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if h.Type != TypeTransportSpecificFeedback || h.Count != FormatRRR { + return errWrongType + } + + p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + return nil +} + +func (p *RapidResynchronizationRequest) len() int { + return headerLength + rrrHeaderLength +} + +// Header returns the Header associated with this packet. +func (p *RapidResynchronizationRequest) Header() Header { + return Header{ + Count: FormatRRR, + Type: TypeTransportSpecificFeedback, + Length: rrrLength, + } +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *RapidResynchronizationRequest) DestinationSSRC() []uint32 { + return []uint32{p.MediaSSRC} +} + +func (p *RapidResynchronizationRequest) String() string { + return fmt.Sprintf("RapidResynchronizationRequest %x %x", p.SenderSSRC, p.MediaSSRC) +} diff --git a/vendor/github.com/pion/rtcp/raw_packet.go b/vendor/github.com/pion/rtcp/raw_packet.go new file mode 100644 index 0000000..3cb6eaf --- /dev/null +++ b/vendor/github.com/pion/rtcp/raw_packet.go @@ -0,0 +1,44 @@ +package rtcp + +import "fmt" + +// RawPacket represents an unparsed RTCP packet. It's returned by Unmarshal when +// 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 +} + +// Unmarshal decodes the packet from binary. +func (r *RawPacket) Unmarshal(b []byte) error { + if len(b) < (headerLength) { + return errPacketTooShort + } + *r = b + + var h Header + return h.Unmarshal(b) +} + +// Header returns the Header associated with this packet. +func (r RawPacket) Header() Header { + var h Header + if err := h.Unmarshal(r); err != nil { + return Header{} + } + return h +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (r *RawPacket) DestinationSSRC() []uint32 { + return []uint32{} +} + +func (r RawPacket) String() string { + out := fmt.Sprintf("RawPacket: %v", ([]byte)(r)) + return out +} diff --git a/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go b/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go new file mode 100644 index 0000000..d37f49e --- /dev/null +++ b/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go @@ -0,0 +1,284 @@ +package rtcp + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/bits" +) + +// ReceiverEstimatedMaximumBitrate contains the receiver's estimated maximum bitrate. +// see: https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 +type ReceiverEstimatedMaximumBitrate struct { + // SSRC of sender + SenderSSRC uint32 + + // Estimated maximum bitrate + Bitrate uint64 + + // 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. + buf = make([]byte, p.MarshalSize()) + + // Write to our buffer. + n, err := p.MarshalTo(buf) + if err != nil { + return nil, err + } + + // This will always be true but just to be safe. + if n != len(buf) { + return nil, errWrongMarshalSize + } + + return buf, nil +} + +// MarshalSize returns the size of the packet when marshaled. +// This can be used in conjunction with `MarshalTo` to avoid allocations. +func (p ReceiverEstimatedMaximumBitrate) MarshalSize() (n int) { + return 20 + 4*len(p.SSRCs) +} + +// MarshalTo serializes the packet to the given byte slice. +func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error) { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT=15 | PT=206 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unique identifier 'R' 'E' 'M' 'B' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Num SSRC | BR Exp | BR Mantissa | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC feedback | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... | + */ + + size := p.MarshalSize() + if len(buf) < size { + return 0, errPacketTooShort + } + + buf[0] = 143 // v=2, p=0, fmt=15 + buf[1] = 206 + + // Length of this packet in 32-bit words minus one. + length := uint16((p.MarshalSize() / 4) - 1) + binary.BigEndian.PutUint16(buf[2:4], length) + + binary.BigEndian.PutUint32(buf[4:8], p.SenderSSRC) + binary.BigEndian.PutUint32(buf[8:12], 0) // always zero + + // ALL HAIL REMB + buf[12] = 'R' + buf[13] = 'E' + buf[14] = 'M' + buf[15] = 'B' + + // 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. + + // 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 + } + + // 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[18] = byte(mantissa >> 8) + buf[19] = byte(mantissa) + + // Write the SSRCs at the very end. + n = 20 + for _, ssrc := range p.SSRCs { + binary.BigEndian.PutUint32(buf[n:n+4], ssrc) + n += 4 + } + + return n, nil +} + +// Unmarshal reads a REMB packet from the given byte slice. +func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) { + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| FMT=15 | PT=206 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of media source | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unique identifier 'R' 'E' 'M' 'B' | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Num SSRC | BR Exp | BR Mantissa | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC feedback | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... | + */ + + // 20 bytes is the size of the packet with no SSRCs + if len(buf) < 20 { + return errPacketTooShort + } + + // version must be 2 + version := buf[0] >> 6 + if version != 2 { + return fmt.Errorf("%w expected(2) actual(%d)", errBadVersion, version) + } + + // padding must be unset + padding := (buf[0] >> 5) & 1 + if padding != 0 { + return fmt.Errorf("%w expected(0) actual(%d)", errWrongPadding, padding) + } + + // fmt must be 15 + fmtVal := buf[0] & 31 + if fmtVal != 15 { + return fmt.Errorf("%w expected(15) actual(%d)", errWrongFeedbackType, fmtVal) + } + + // Must be payload specific feedback + if buf[1] != 206 { + return fmt.Errorf("%w expected(206) actual(%d)", errWrongPayloadType, buf[1]) + } + + // length is the number of 32-bit words, minus 1 + length := binary.BigEndian.Uint16(buf[2:4]) + size := int((length + 1) * 4) + + // There's not way this could be legit + if size < 20 { + return errHeaderTooSmall + } + + // Make sure the buffer is large enough. + if len(buf) < size { + return errPacketTooShort + } + + // The sender SSRC is 32-bits + p.SenderSSRC = binary.BigEndian.Uint32(buf[4:8]) + + // The destination SSRC must be 0 + media := binary.BigEndian.Uint32(buf[8:12]) + if media != 0 { + return errSSRCMustBeZero + } + + // REMB rules all around me + if !bytes.Equal(buf[12:16], []byte{'R', 'E', 'M', 'B'}) { + return errMissingREMBidentifier + } + + // The next byte is the number of SSRC entries at the end. + num := int(buf[16]) + + // Now we know the expected size, make sure they match. + if size != 20+4*num { + return errSSRCNumAndLengthMismatch + } + + // Get the 6-bit exponent value. + exp := buf[17] >> 2 + + // 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]) + + // 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 + } + + // Clear any existing SSRCs + p.SSRCs = nil + + // Loop over and parse the SSRC entires at the end. + // We already verified that size == num * 4 + for n := 20; n < size; n += 4 { + ssrc := binary.BigEndian.Uint32(buf[n : n+4]) + p.SSRCs = append(p.SSRCs, ssrc) + } + + return nil +} + +// Header returns the Header associated with this packet. +func (p *ReceiverEstimatedMaximumBitrate) Header() Header { + return Header{ + Count: FormatREMB, + Type: TypePayloadSpecificFeedback, + Length: uint16((p.MarshalSize() / 4) - 1), + } +} + +// String prints the REMB packet in a human-readable format. +func (p *ReceiverEstimatedMaximumBitrate) String() string { + // Keep a table of powers to units for fast conversion. + 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) + powers := 0 + + // Keep dividing the bitrate until it's under 1000 + for bitrate >= 1000.0 && powers < len(bitUnits) { + bitrate /= 1000.0 + powers++ + } + + unit := bitUnits[powers] + + return fmt.Sprintf("ReceiverEstimatedMaximumBitrate %x %.2f %s/s", p.SenderSSRC, bitrate, unit) +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *ReceiverEstimatedMaximumBitrate) DestinationSSRC() []uint32 { + return p.SSRCs +} diff --git a/vendor/github.com/pion/rtcp/receiver_report.go b/vendor/github.com/pion/rtcp/receiver_report.go new file mode 100644 index 0000000..cf28d39 --- /dev/null +++ b/vendor/github.com/pion/rtcp/receiver_report.go @@ -0,0 +1,193 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// A ReceiverReport (RR) packet provides reception quality feedback for an RTP stream +type ReceiverReport struct { + // The synchronization source identifier for the originator of this RR packet. + SSRC uint32 + // Zero or more reception report blocks depending on the number of other + // sources heard by this sender since the last report. Each reception report + // block conveys statistics on the reception of RTP packets from a + // single synchronization source. + Reports []ReceptionReport + // Extension contains additional, payload-specific information that needs to + // be reported regularly about the receiver. + ProfileExtensions []byte +} + +var _ Packet = (*ReceiverReport)(nil) // assert is a Packet + +const ( + ssrcLength = 4 + rrSSRCOffset = headerLength + rrReportOffset = rrSSRCOffset + ssrcLength +) + +// Marshal encodes the ReceiverReport in binary +func (r ReceiverReport) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| RC | PT=RR=201 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC of packet sender | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_1 (SSRC of first source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 1 | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_2 (SSRC of second source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 2 : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | profile-specific extensions | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + rawPacket := make([]byte, r.len()) + packetBody := rawPacket[headerLength:] + + binary.BigEndian.PutUint32(packetBody, r.SSRC) + + for i, rp := range r.Reports { + data, err := rp.Marshal() + if err != nil { + return nil, err + } + offset := ssrcLength + receptionReportLength*i + copy(packetBody[offset:], data) + } + + if len(r.Reports) > countMax { + return nil, errTooManyReports + } + + pe := make([]byte, len(r.ProfileExtensions)) + copy(pe, r.ProfileExtensions) + + // if the length of the profile extensions isn't devisible + // by 4, we need to pad the end. + for (len(pe) & 0x3) != 0 { + pe = append(pe, 0) + } + + rawPacket = append(rawPacket, pe...) + + hData, err := r.Header().Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the ReceiverReport from binary +func (r *ReceiverReport) Unmarshal(rawPacket []byte) error { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| RC | PT=RR=201 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC of packet sender | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_1 (SSRC of first source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 1 | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_2 (SSRC of second source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 2 : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | profile-specific extensions | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + if len(rawPacket) < (headerLength + ssrcLength) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if h.Type != TypeReceiverReport { + return errWrongType + } + + r.SSRC = binary.BigEndian.Uint32(rawPacket[rrSSRCOffset:]) + + for i := rrReportOffset; i < len(rawPacket) && len(r.Reports) < int(h.Count); i += receptionReportLength { + var rr ReceptionReport + if err := rr.Unmarshal(rawPacket[i:]); err != nil { + return err + } + r.Reports = append(r.Reports, rr) + } + r.ProfileExtensions = rawPacket[rrReportOffset+(len(r.Reports)*receptionReportLength):] + + if uint8(len(r.Reports)) != h.Count { + return errInvalidHeader + } + + return nil +} + +func (r *ReceiverReport) len() int { + repsLength := 0 + for _, rep := range r.Reports { + repsLength += rep.len() + } + return headerLength + ssrcLength + repsLength +} + +// Header returns the Header associated with this packet. +func (r *ReceiverReport) Header() Header { + return Header{ + Count: uint8(len(r.Reports)), + Type: TypeReceiverReport, + Length: uint16((r.len()/4)-1) + uint16(getPadding(len(r.ProfileExtensions))), + } +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (r *ReceiverReport) DestinationSSRC() []uint32 { + out := make([]uint32, len(r.Reports)) + for i, v := range r.Reports { + out[i] = v.SSRC + } + return out +} + +func (r ReceiverReport) String() string { + out := fmt.Sprintf("ReceiverReport from %x\n", r.SSRC) + out += "\tSSRC \tLost\tLastSequence\n" + for _, i := range r.Reports { + out += fmt.Sprintf("\t%x\t%d/%d\t%d\n", i.SSRC, i.FractionLost, i.TotalLost, i.LastSequenceNumber) + } + out += fmt.Sprintf("\tProfile Extension Data: %v\n", r.ProfileExtensions) + return out +} diff --git a/vendor/github.com/pion/rtcp/reception_report.go b/vendor/github.com/pion/rtcp/reception_report.go new file mode 100644 index 0000000..5bff8f2 --- /dev/null +++ b/vendor/github.com/pion/rtcp/reception_report.go @@ -0,0 +1,130 @@ +package rtcp + +import "encoding/binary" + +// A ReceptionReport block conveys statistics on the reception of RTP packets +// from a single synchronization source. +type ReceptionReport struct { + // The SSRC identifier of the source to which the information in this + // reception report block pertains. + SSRC uint32 + // The fraction of RTP data packets from source SSRC lost since the + // previous SR or RR packet was sent, expressed as a fixed point + // number with the binary point at the left edge of the field. + FractionLost uint8 + // The total number of RTP data packets from source SSRC that have + // been lost since the beginning of reception. + TotalLost uint32 + // The low 16 bits contain the highest sequence number received in an + // RTP data packet from source SSRC, and the most significant 16 + // bits extend that sequence number with the corresponding count of + // sequence number cycles. + LastSequenceNumber uint32 + // An estimate of the statistical variance of the RTP data packet + // interarrival time, measured in timestamp units and expressed as an + // unsigned integer. + Jitter uint32 + // The middle 32 bits out of 64 in the NTP timestamp received as part of + // the most recent RTCP sender report (SR) packet from source SSRC. If no + // SR has been received yet, the field is set to zero. + LastSenderReport uint32 + // The delay, expressed in units of 1/65536 seconds, between receiving the + // last SR packet from source SSRC and sending this reception report block. + // If no SR packet has been received yet from SSRC, the field is set to zero. + Delay uint32 +} + +const ( + receptionReportLength = 24 + fractionLostOffset = 4 + totalLostOffset = 5 + lastSeqOffset = 8 + jitterOffset = 12 + lastSROffset = 16 + delayOffset = 20 +) + +// Marshal encodes the ReceptionReport in binary +func (r ReceptionReport) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | SSRC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + rawPacket := make([]byte, receptionReportLength) + + binary.BigEndian.PutUint32(rawPacket, r.SSRC) + + rawPacket[fractionLostOffset] = r.FractionLost + + // pack TotalLost into 24 bits + if r.TotalLost >= (1 << 25) { + return nil, errInvalidTotalLost + } + tlBytes := rawPacket[totalLostOffset:] + tlBytes[0] = byte(r.TotalLost >> 16) + tlBytes[1] = byte(r.TotalLost >> 8) + tlBytes[2] = byte(r.TotalLost) + + binary.BigEndian.PutUint32(rawPacket[lastSeqOffset:], r.LastSequenceNumber) + binary.BigEndian.PutUint32(rawPacket[jitterOffset:], r.Jitter) + binary.BigEndian.PutUint32(rawPacket[lastSROffset:], r.LastSenderReport) + binary.BigEndian.PutUint32(rawPacket[delayOffset:], r.Delay) + + return rawPacket, nil +} + +// Unmarshal decodes the ReceptionReport from binary +func (r *ReceptionReport) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < receptionReportLength { + return errPacketTooShort + } + + /* + * 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 + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | SSRC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + r.SSRC = binary.BigEndian.Uint32(rawPacket) + r.FractionLost = rawPacket[fractionLostOffset] + + tlBytes := rawPacket[totalLostOffset:] + r.TotalLost = uint32(tlBytes[2]) | uint32(tlBytes[1])<<8 | uint32(tlBytes[0])<<16 + + r.LastSequenceNumber = binary.BigEndian.Uint32(rawPacket[lastSeqOffset:]) + r.Jitter = binary.BigEndian.Uint32(rawPacket[jitterOffset:]) + r.LastSenderReport = binary.BigEndian.Uint32(rawPacket[lastSROffset:]) + r.Delay = binary.BigEndian.Uint32(rawPacket[delayOffset:]) + + return nil +} + +func (r *ReceptionReport) len() int { + return receptionReportLength +} diff --git a/vendor/github.com/pion/rtcp/renovate.json b/vendor/github.com/pion/rtcp/renovate.json new file mode 100644 index 0000000..4400fd9 --- /dev/null +++ b/vendor/github.com/pion/rtcp/renovate.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "config:base" + ], + "postUpdateOptions": [ + "gomodTidy" + ], + "commitBody": "Generated by renovateBot", + "packageRules": [ + { + "packagePatterns": ["^golang.org/x/"], + "schedule": ["on the first day of the month"] + } + ] +} diff --git a/vendor/github.com/pion/rtcp/sender_report.go b/vendor/github.com/pion/rtcp/sender_report.go new file mode 100644 index 0000000..1b16380 --- /dev/null +++ b/vendor/github.com/pion/rtcp/sender_report.go @@ -0,0 +1,260 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// A SenderReport (SR) packet provides reception quality feedback for an RTP stream +type SenderReport struct { + // The synchronization source identifier for the originator of this SR packet. + SSRC uint32 + // The wallclock time when this report was sent so that it may be used in + // combination with timestamps returned in reception reports from other + // receivers to measure round-trip propagation to those receivers. + NTPTime uint64 + // Corresponds to the same time as the NTP timestamp (above), but in + // the same units and with the same random offset as the RTP + // timestamps in data packets. This correspondence may be used for + // intra- and inter-media synchronization for sources whose NTP + // timestamps are synchronized, and may be used by media-independent + // receivers to estimate the nominal RTP clock frequency. + RTPTime uint32 + // The total number of RTP data packets transmitted by the sender + // since starting transmission up until the time this SR packet was + // generated. + PacketCount uint32 + // The total number of payload octets (i.e., not including header or + // padding) transmitted in RTP data packets by the sender since + // starting transmission up until the time this SR packet was + // generated. + OctetCount uint32 + // Zero or more reception report blocks depending on the number of other + // sources heard by this sender since the last report. Each reception report + // block conveys statistics on the reception of RTP packets from a + // single synchronization source. + Reports []ReceptionReport + // ProfileExtensions contains additional, payload-specific information that needs to + // be reported regularly about the sender. + ProfileExtensions []byte +} + +var _ Packet = (*SenderReport)(nil) // assert is a Packet + +const ( + srHeaderLength = 24 + srSSRCOffset = 0 + srNTPOffset = srSSRCOffset + ssrcLength + ntpTimeLength = 8 + srRTPOffset = srNTPOffset + ntpTimeLength + rtpTimeLength = 4 + srPacketCountOffset = srRTPOffset + rtpTimeLength + srPacketCountLength = 4 + srOctetCountOffset = srPacketCountOffset + srPacketCountLength + srOctetCountLength = 4 + srReportOffset = srOctetCountOffset + srOctetCountLength +) + +// Marshal encodes the SenderReport in binary +func (r SenderReport) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| RC | PT=SR=200 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC of sender | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * sender | NTP timestamp, most significant word | + * info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NTP timestamp, least significant word | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RTP timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | sender's packet count | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | sender's octet count | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_1 (SSRC of first source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 1 | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_2 (SSRC of second source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 2 : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | profile-specific extensions | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + rawPacket := make([]byte, r.len()) + packetBody := rawPacket[headerLength:] + + binary.BigEndian.PutUint32(packetBody[srSSRCOffset:], r.SSRC) + binary.BigEndian.PutUint64(packetBody[srNTPOffset:], r.NTPTime) + binary.BigEndian.PutUint32(packetBody[srRTPOffset:], r.RTPTime) + binary.BigEndian.PutUint32(packetBody[srPacketCountOffset:], r.PacketCount) + binary.BigEndian.PutUint32(packetBody[srOctetCountOffset:], r.OctetCount) + + offset := srHeaderLength + for _, rp := range r.Reports { + data, err := rp.Marshal() + if err != nil { + return nil, err + } + copy(packetBody[offset:], data) + offset += receptionReportLength + } + + if len(r.Reports) > countMax { + return nil, errTooManyReports + } + + copy(packetBody[offset:], r.ProfileExtensions) + + hData, err := r.Header().Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the SenderReport from binary +func (r *SenderReport) Unmarshal(rawPacket []byte) error { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| RC | PT=SR=200 | length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SSRC of sender | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * sender | NTP timestamp, most significant word | + * info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NTP timestamp, least significant word | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RTP timestamp | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | sender's packet count | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | sender's octet count | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_1 (SSRC of first source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 1 | fraction lost | cumulative number of packets lost | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | extended highest sequence number received | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | interarrival jitter | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | last SR (LSR) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | delay since last SR (DLSR) | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * report | SSRC_2 (SSRC of second source) | + * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 2 : ... : + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | profile-specific extensions | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + if len(rawPacket) < (headerLength + srHeaderLength) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if h.Type != TypeSenderReport { + return errWrongType + } + + packetBody := rawPacket[headerLength:] + + r.SSRC = binary.BigEndian.Uint32(packetBody[srSSRCOffset:]) + r.NTPTime = binary.BigEndian.Uint64(packetBody[srNTPOffset:]) + r.RTPTime = binary.BigEndian.Uint32(packetBody[srRTPOffset:]) + r.PacketCount = binary.BigEndian.Uint32(packetBody[srPacketCountOffset:]) + r.OctetCount = binary.BigEndian.Uint32(packetBody[srOctetCountOffset:]) + + offset := srReportOffset + for i := 0; i < int(h.Count); i++ { + rrEnd := offset + receptionReportLength + if rrEnd > len(packetBody) { + return errPacketTooShort + } + rrBody := packetBody[offset : offset+receptionReportLength] + offset = rrEnd + + var rr ReceptionReport + if err := rr.Unmarshal(rrBody); err != nil { + return err + } + r.Reports = append(r.Reports, rr) + } + + if offset < len(packetBody) { + r.ProfileExtensions = packetBody[offset:] + } + + if uint8(len(r.Reports)) != h.Count { + return errInvalidHeader + } + + return nil +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (r *SenderReport) DestinationSSRC() []uint32 { + out := make([]uint32, len(r.Reports)+1) + for i, v := range r.Reports { + out[i] = v.SSRC + } + out[len(r.Reports)] = r.SSRC + return out +} + +func (r *SenderReport) len() int { + repsLength := 0 + for _, rep := range r.Reports { + repsLength += rep.len() + } + return headerLength + srHeaderLength + repsLength + len(r.ProfileExtensions) +} + +// Header returns the Header associated with this packet. +func (r *SenderReport) Header() Header { + return Header{ + Count: uint8(len(r.Reports)), + Type: TypeSenderReport, + Length: uint16((r.len() / 4) - 1), + } +} + +func (r SenderReport) String() string { + out := fmt.Sprintf("SenderReport from %x\n", r.SSRC) + out += fmt.Sprintf("\tNTPTime:\t%d\n", r.NTPTime) + out += fmt.Sprintf("\tRTPTIme:\t%d\n", r.RTPTime) + out += fmt.Sprintf("\tPacketCount:\t%d\n", r.PacketCount) + out += fmt.Sprintf("\tOctetCount:\t%d\n", r.OctetCount) + + out += "\tSSRC \tLost\tLastSequence\n" + for _, i := range r.Reports { + out += fmt.Sprintf("\t%x\t%d/%d\t%d\n", i.SSRC, i.FractionLost, i.TotalLost, i.LastSequenceNumber) + } + out += fmt.Sprintf("\tProfile Extension Data: %v\n", r.ProfileExtensions) + return out +} diff --git a/vendor/github.com/pion/rtcp/slice_loss_indication.go b/vendor/github.com/pion/rtcp/slice_loss_indication.go new file mode 100644 index 0000000..7689309 --- /dev/null +++ b/vendor/github.com/pion/rtcp/slice_loss_indication.go @@ -0,0 +1,115 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" + "math" +) + +// SLIEntry represents a single entry to the SLI packet's +// list of lost slices. +type SLIEntry struct { + // ID of first lost slice + First uint16 + + // Number of lost slices + Number uint16 + + // ID of related picture + Picture uint8 +} + +// The SliceLossIndication packet informs the encoder about the loss of a picture slice +type SliceLossIndication struct { + // SSRC of sender + SenderSSRC uint32 + + // SSRC of the media source + MediaSSRC uint32 + + SLI []SLIEntry +} + +var _ Packet = (*SliceLossIndication)(nil) // assert is a Packet + +const ( + sliLength = 2 + sliOffset = 8 +) + +// Marshal encodes the SliceLossIndication in binary +func (p SliceLossIndication) Marshal() ([]byte, error) { + if len(p.SLI)+sliLength > math.MaxUint8 { + return nil, errTooManyReports + } + + rawPacket := make([]byte, sliOffset+(len(p.SLI)*4)) + binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC) + binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC) + for i, s := range p.SLI { + sli := ((uint32(s.First) & 0x1FFF) << 19) | + ((uint32(s.Number) & 0x1FFF) << 6) | + (uint32(s.Picture) & 0x3F) + binary.BigEndian.PutUint32(rawPacket[sliOffset+(4*i):], sli) + } + hData, err := p.Header().Marshal() + if err != nil { + return nil, err + } + + return append(hData, rawPacket...), nil +} + +// Unmarshal decodes the SliceLossIndication from binary +func (p *SliceLossIndication) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < (headerLength + ssrcLength) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if len(rawPacket) < (headerLength + int(4*h.Length)) { + return errPacketTooShort + } + + if h.Type != TypeTransportSpecificFeedback || h.Count != FormatSLI { + return errWrongType + } + + p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + for i := headerLength + sliOffset; i < (headerLength + int(h.Length*4)); i += 4 { + sli := binary.BigEndian.Uint32(rawPacket[i:]) + p.SLI = append(p.SLI, SLIEntry{ + First: uint16((sli >> 19) & 0x1FFF), + Number: uint16((sli >> 6) & 0x1FFF), + Picture: uint8(sli & 0x3F), + }) + } + return nil +} + +func (p *SliceLossIndication) len() int { + return headerLength + sliOffset + (len(p.SLI) * 4) +} + +// Header returns the Header associated with this packet. +func (p *SliceLossIndication) Header() Header { + return Header{ + Count: FormatSLI, + Type: TypeTransportSpecificFeedback, + Length: uint16((p.len() / 4) - 1), + } +} + +func (p *SliceLossIndication) String() string { + return fmt.Sprintf("SliceLossIndication %x %x %+v", p.SenderSSRC, p.MediaSSRC, p.SLI) +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *SliceLossIndication) DestinationSSRC() []uint32 { + return []uint32{p.MediaSSRC} +} diff --git a/vendor/github.com/pion/rtcp/source_description.go b/vendor/github.com/pion/rtcp/source_description.go new file mode 100644 index 0000000..4306b66 --- /dev/null +++ b/vendor/github.com/pion/rtcp/source_description.go @@ -0,0 +1,352 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" +) + +// SDESType is the item type used in the RTCP SDES control packet. +type SDESType uint8 + +// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5 +const ( + SDESEnd SDESType = iota // end of SDES list RFC 3550, 6.5 + SDESCNAME // canonical name RFC 3550, 6.5.1 + SDESName // user name RFC 3550, 6.5.2 + SDESEmail // user's electronic mail address RFC 3550, 6.5.3 + SDESPhone // user's phone number RFC 3550, 6.5.4 + SDESLocation // geographic user location RFC 3550, 6.5.5 + SDESTool // name of application or tool RFC 3550, 6.5.6 + SDESNote // notice about the source RFC 3550, 6.5.7 + SDESPrivate // private extensions RFC 3550, 6.5.8 (not implemented) +) + +func (s SDESType) String() string { + switch s { + case SDESEnd: + return "END" + case SDESCNAME: + return "CNAME" + case SDESName: + return "NAME" + case SDESEmail: + return "EMAIL" + case SDESPhone: + return "PHONE" + case SDESLocation: + return "LOC" + case SDESTool: + return "TOOL" + case SDESNote: + return "NOTE" + case SDESPrivate: + return "PRIV" + default: + return string(s) + } +} + +const ( + sdesSourceLen = 4 + sdesTypeLen = 1 + sdesTypeOffset = 0 + sdesOctetCountLen = 1 + sdesOctetCountOffset = 1 + sdesMaxOctetCount = (1 << 8) - 1 + sdesTextOffset = 2 +) + +// A SourceDescription (SDES) packet describes the sources in an RTP stream. +type SourceDescription struct { + Chunks []SourceDescriptionChunk +} + +var _ Packet = (*SourceDescription)(nil) // assert is a Packet + +// Marshal encodes the SourceDescription in binary +func (s SourceDescription) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| SC | PT=SDES=202 | length | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * chunk | SSRC/CSRC_1 | + * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * chunk | SSRC/CSRC_2 | + * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + rawPacket := make([]byte, s.len()) + packetBody := rawPacket[headerLength:] + + chunkOffset := 0 + for _, c := range s.Chunks { + data, err := c.Marshal() + if err != nil { + return nil, err + } + copy(packetBody[chunkOffset:], data) + chunkOffset += len(data) + } + + if len(s.Chunks) > countMax { + return nil, errTooManyChunks + } + + hData, err := s.Header().Marshal() + if err != nil { + return nil, err + } + copy(rawPacket, hData) + + return rawPacket, nil +} + +// Unmarshal decodes the SourceDescription from binary +func (s *SourceDescription) Unmarshal(rawPacket []byte) error { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * header |V=2|P| SC | PT=SDES=202 | length | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * chunk | SSRC/CSRC_1 | + * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * chunk | SSRC/CSRC_2 | + * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if h.Type != TypeSourceDescription { + return errWrongType + } + + for i := headerLength; i < len(rawPacket); { + var chunk SourceDescriptionChunk + if err := chunk.Unmarshal(rawPacket[i:]); err != nil { + return err + } + s.Chunks = append(s.Chunks, chunk) + + i += chunk.len() + } + + if len(s.Chunks) != int(h.Count) { + return errInvalidHeader + } + + return nil +} + +func (s *SourceDescription) len() int { + chunksLength := 0 + for _, c := range s.Chunks { + chunksLength += c.len() + } + return headerLength + chunksLength +} + +// Header returns the Header associated with this packet. +func (s *SourceDescription) Header() Header { + return Header{ + Count: uint8(len(s.Chunks)), + Type: TypeSourceDescription, + Length: uint16((s.len() / 4) - 1), + } +} + +// A SourceDescriptionChunk contains items describing a single RTP source +type SourceDescriptionChunk struct { + // The source (ssrc) or contributing source (csrc) identifier this packet describes + Source uint32 + Items []SourceDescriptionItem +} + +// Marshal encodes the SourceDescriptionChunk in binary +func (s SourceDescriptionChunk) Marshal() ([]byte, error) { + /* + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | SSRC/CSRC_1 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + rawPacket := make([]byte, sdesSourceLen) + binary.BigEndian.PutUint32(rawPacket, s.Source) + + for _, it := range s.Items { + data, err := it.Marshal() + if err != nil { + return nil, err + } + rawPacket = append(rawPacket, data...) + } + + // The list of items in each chunk MUST be terminated by one or more null octets + rawPacket = append(rawPacket, uint8(SDESEnd)) + + // additional null octets MUST be included if needed to pad until the next 32-bit boundary + rawPacket = append(rawPacket, make([]byte, getPadding(len(rawPacket)))...) + + return rawPacket, nil +} + +// Unmarshal decodes the SourceDescriptionChunk from binary +func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error { + /* + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + * | SSRC/CSRC_1 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SDES items | + * | ... | + * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + + if len(rawPacket) < (sdesSourceLen + sdesTypeLen) { + return errPacketTooShort + } + + s.Source = binary.BigEndian.Uint32(rawPacket) + + for i := 4; i < len(rawPacket); { + if pktType := SDESType(rawPacket[i]); pktType == SDESEnd { + return nil + } + + var it SourceDescriptionItem + if err := it.Unmarshal(rawPacket[i:]); err != nil { + return err + } + s.Items = append(s.Items, it) + i += it.len() + } + + return errPacketTooShort +} + +func (s SourceDescriptionChunk) len() int { + len := sdesSourceLen + for _, it := range s.Items { + len += it.len() + } + len += sdesTypeLen // for terminating null octet + + // align to 32-bit boundary + len += getPadding(len) + + return len +} + +// A SourceDescriptionItem is a part of a SourceDescription that describes a stream. +type SourceDescriptionItem struct { + // The type identifier for this item. eg, SDESCNAME for canonical name description. + // + // Type zero or SDESEnd is interpreted as the end of an item list and cannot be used. + Type SDESType + // Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type. + Text string +} + +func (s SourceDescriptionItem) len() int { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | CNAME=1 | length | user and domain name ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + return sdesTypeLen + sdesOctetCountLen + len([]byte(s.Text)) +} + +// Marshal encodes the SourceDescriptionItem in binary +func (s SourceDescriptionItem) Marshal() ([]byte, error) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | CNAME=1 | length | user and domain name ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + if s.Type == SDESEnd { + return nil, errSDESMissingType + } + + rawPacket := make([]byte, sdesTypeLen+sdesOctetCountLen) + + rawPacket[sdesTypeOffset] = uint8(s.Type) + + txtBytes := []byte(s.Text) + octetCount := len(txtBytes) + if octetCount > sdesMaxOctetCount { + return nil, errSDESTextTooLong + } + rawPacket[sdesOctetCountOffset] = uint8(octetCount) + + rawPacket = append(rawPacket, txtBytes...) + + return rawPacket, nil +} + +// Unmarshal decodes the SourceDescriptionItem from binary +func (s *SourceDescriptionItem) Unmarshal(rawPacket []byte) error { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | CNAME=1 | length | user and domain name ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + if len(rawPacket) < (sdesTypeLen + sdesOctetCountLen) { + return errPacketTooShort + } + + s.Type = SDESType(rawPacket[sdesTypeOffset]) + + octetCount := int(rawPacket[sdesOctetCountOffset]) + if sdesTextOffset+octetCount > len(rawPacket) { + return errPacketTooShort + } + + txtBytes := rawPacket[sdesTextOffset : sdesTextOffset+octetCount] + s.Text = string(txtBytes) + + return nil +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (s *SourceDescription) DestinationSSRC() []uint32 { + out := make([]uint32, len(s.Chunks)) + for i, v := range s.Chunks { + out[i] = v.Source + } + return out +} + +func (s *SourceDescription) String() string { + out := "Source Description:\n" + for _, c := range s.Chunks { + out += fmt.Sprintf("\t%x: %s\n", c.Source, c.Items) + } + return out +} diff --git a/vendor/github.com/pion/rtcp/transport_layer_cc.go b/vendor/github.com/pion/rtcp/transport_layer_cc.go new file mode 100644 index 0000000..9606581 --- /dev/null +++ b/vendor/github.com/pion/rtcp/transport_layer_cc.go @@ -0,0 +1,560 @@ +package rtcp + +// Author: adwpc + +import ( + "encoding/binary" + "errors" + "fmt" + "math" +) + +// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT=15 | PT=205 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | base sequence number | packet status count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | reference time | fb pkt. count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | packet chunk | packet chunk | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | packet chunk | recv delta | recv delta | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | recv delta | recv delta | zero padding | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// for packet status chunk +const ( + // type of packet status chunk + TypeTCCRunLengthChunk = 0 + TypeTCCStatusVectorChunk = 1 + + // len of packet status chunk + packetStatusChunkLength = 2 +) + +// type of packet status symbol and recv delta +const ( + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.1 + TypeTCCPacketNotReceived = uint16(iota) + TypeTCCPacketReceivedSmallDelta + TypeTCCPacketReceivedLargeDelta + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-7 + // see Example 2: "packet received, w/o recv delta" + TypeTCCPacketReceivedWithoutDelta +) + +// for status vector chunk +const ( + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.4 + TypeTCCSymbolSizeOneBit = 0 + TypeTCCSymbolSizeTwoBit = 1 + + // Notice: RFC is wrong: "packet received" (0) and "packet not received" (1) + // if S == TypeTCCSymbolSizeOneBit, symbol list will be: TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta + // if S == TypeTCCSymbolSizeTwoBit, symbol list will be same as above: +) + +func numOfBitsOfSymbolSize() map[uint16]uint16 { + return map[uint16]uint16{ + TypeTCCSymbolSizeOneBit: 1, + TypeTCCSymbolSizeTwoBit: 2, + } +} + +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") +) + +// PacketStatusChunk has two kinds: +// RunLengthChunk and StatusVectorChunk +type PacketStatusChunk interface { + Marshal() ([]byte, error) + Unmarshal(rawPacket []byte) error +} + +// RunLengthChunk T=TypeTCCRunLengthChunk +// 0 1 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |T| S | Run Length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type RunLengthChunk struct { + PacketStatusChunk + + // T = TypeTCCRunLengthChunk + Type uint16 + + // S: type of packet status + // kind: TypeTCCPacketNotReceived or... + PacketStatusSymbol uint16 + + // RunLength: count of S + RunLength uint16 +} + +// Marshal .. +func (r RunLengthChunk) Marshal() ([]byte, error) { + chunk := make([]byte, 2) + + // append 1 bit '0' + dst, err := setNBitsOfUint16(0, 1, 0, 0) + if err != nil { + return nil, err + } + + // append 2 bit PacketStatusSymbol + dst, err = setNBitsOfUint16(dst, 2, 1, r.PacketStatusSymbol) + if err != nil { + return nil, err + } + + // append 13 bit RunLength + dst, err = setNBitsOfUint16(dst, 13, 3, r.RunLength) + if err != nil { + return nil, err + } + + binary.BigEndian.PutUint16(chunk, dst) + return chunk, nil +} + +// Unmarshal .. +func (r *RunLengthChunk) Unmarshal(rawPacket []byte) error { + if len(rawPacket) != packetStatusChunkLength { + return errPacketStatusChunkLength + } + + // record type + r.Type = TypeTCCRunLengthChunk + + // get PacketStatusSymbol + // r.PacketStatusSymbol = uint16(rawPacket[0] >> 5 & 0x03) + r.PacketStatusSymbol = getNBitsFromByte(rawPacket[0], 1, 2) + + // get RunLength + // r.RunLength = uint16(rawPacket[0]&0x1F)*256 + uint16(rawPacket[1]) + r.RunLength = getNBitsFromByte(rawPacket[0], 3, 5)<<8 + uint16(rawPacket[1]) + return nil +} + +// StatusVectorChunk T=typeStatusVecotrChunk +// 0 1 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |T|S| symbol list | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +type StatusVectorChunk struct { + PacketStatusChunk + // T = TypeTCCRunLengthChunk + Type uint16 + + // TypeTCCSymbolSizeOneBit or TypeTCCSymbolSizeTwoBit + SymbolSize uint16 + + // when SymbolSize = TypeTCCSymbolSizeOneBit, SymbolList is 14*1bit: + // TypeTCCSymbolListPacketReceived or TypeTCCSymbolListPacketNotReceived + // when SymbolSize = TypeTCCSymbolSizeTwoBit, SymbolList is 7*2bit: + // TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta TypeTCCPacketReceivedLargeDelta or typePacketReserved + SymbolList []uint16 +} + +// Marshal .. +func (r StatusVectorChunk) Marshal() ([]byte, error) { + chunk := make([]byte, 2) + + // set first bit '1' + dst, err := setNBitsOfUint16(0, 1, 0, 1) + if err != nil { + return nil, err + } + + // set second bit SymbolSize + dst, err = setNBitsOfUint16(dst, 1, 1, r.SymbolSize) + if err != nil { + return nil, err + } + + numOfBits := numOfBitsOfSymbolSize()[r.SymbolSize] + // append 14 bit SymbolList + for i, s := range r.SymbolList { + index := numOfBits*uint16(i) + 2 + dst, err = setNBitsOfUint16(dst, numOfBits, index, s) + if err != nil { + return nil, err + } + } + + binary.BigEndian.PutUint16(chunk, dst) + // set SymbolList(bit8-15) + // chunk[1] = uint8(r.SymbolList) & 0x0f + return chunk, nil +} + +// Unmarshal .. +func (r *StatusVectorChunk) Unmarshal(rawPacket []byte) error { + if len(rawPacket) != packetStatusChunkLength { + return errPacketStatusChunkLength + } + + r.Type = TypeTCCStatusVectorChunk + r.SymbolSize = getNBitsFromByte(rawPacket[0], 1, 1) + + if r.SymbolSize == TypeTCCSymbolSizeOneBit { + for i := uint16(0); i < 6; i++ { + r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[0], 2+i, 1)) + } + for i := uint16(0); i < 8; i++ { + r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[1], i, 1)) + } + return nil + } + if r.SymbolSize == TypeTCCSymbolSizeTwoBit { + for i := uint16(0); i < 3; i++ { + r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[0], 2+i*2, 2)) + } + for i := uint16(0); i < 4; i++ { + r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[1], i*2, 2)) + } + return nil + } + + r.SymbolSize = getNBitsFromByte(rawPacket[0], 2, 6)<<8 + uint16(rawPacket[1]) + return nil +} + +const ( + // TypeTCCDeltaScaleFactor https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5 + TypeTCCDeltaScaleFactor = 250 +) + +// RecvDelta are represented as multiples of 250us +// small delta is 1 byte: [0,63.75]ms = [0, 63750]us = [0, 255]*250us +// big delta is 2 bytes: [-8192.0, 8191.75]ms = [-8192000, 8191750]us = [-32768, 32767]*250us +// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5 +type RecvDelta struct { + Type uint16 + // us + Delta int64 +} + +// Marshal .. +func (r RecvDelta) Marshal() ([]byte, error) { + delta := r.Delta / TypeTCCDeltaScaleFactor + + // small delta + if r.Type == TypeTCCPacketReceivedSmallDelta && delta >= 0 && delta <= math.MaxUint8 { + deltaChunk := make([]byte, 1) + deltaChunk[0] = byte(delta) + return deltaChunk, nil + } + + // big delta + if r.Type == TypeTCCPacketReceivedLargeDelta && delta >= math.MinInt16 && delta <= math.MaxInt16 { + deltaChunk := make([]byte, 2) + binary.BigEndian.PutUint16(deltaChunk, uint16(delta)) + return deltaChunk, nil + } + + // overflow + return nil, errDeltaExceedLimit +} + +// Unmarshal .. +func (r *RecvDelta) Unmarshal(rawPacket []byte) error { + chunkLen := len(rawPacket) + + // must be 1 or 2 bytes + if chunkLen != 1 && chunkLen != 2 { + return errDeltaExceedLimit + } + + if chunkLen == 1 { + r.Type = TypeTCCPacketReceivedSmallDelta + r.Delta = TypeTCCDeltaScaleFactor * int64(rawPacket[0]) + return nil + } + + r.Type = TypeTCCPacketReceivedLargeDelta + r.Delta = TypeTCCDeltaScaleFactor * int64(int16(binary.BigEndian.Uint16(rawPacket))) + return nil +} + +const ( + // the offset after header + baseSequenceNumberOffset = 8 + packetStatusCountOffset = 10 + referenceTimeOffset = 12 + fbPktCountOffset = 15 + packetChunkOffset = 16 +) + +// TransportLayerCC for sender-BWE +// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5 +type TransportLayerCC struct { + // header + Header Header + + // SSRC of sender + SenderSSRC uint32 + + // SSRC of the media source + MediaSSRC uint32 + + // Transport wide sequence of rtp extension + BaseSequenceNumber uint16 + + // PacketStatusCount + PacketStatusCount uint16 + + // ReferenceTime + ReferenceTime uint32 + + // FbPktCount + FbPktCount uint8 + + // PacketChunks + PacketChunks []PacketStatusChunk + + // RecvDeltas + RecvDeltas []*RecvDelta +} + +// Header returns the Header associated with this packet. +// func (t *TransportLayerCC) Header() Header { +// return t.Header +// return Header{ +// Padding: true, +// Count: FormatTCC, +// Type: TypeTCCTransportSpecificFeedback, +// // https://tools.ietf.org/html/rfc4585#page-33 +// Length: uint16((t.len() / 4) - 1), +// } +// } + +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 { + n++ + } else { + n += 2 + } + } + return n +} + +// Len return total bytes with padding +func (t *TransportLayerCC) Len() uint16 { + n := t.packetLen() + // has padding + if n%4 != 0 { + n = (n/4 + 1) * 4 + } + + return n +} + +func (t TransportLayerCC) String() string { + out := fmt.Sprintf("TransportLayerCC:\n\tHeader %v\n", t.Header) + out += fmt.Sprintf("TransportLayerCC:\n\tSender Ssrc %d\n", t.SenderSSRC) + out += fmt.Sprintf("\tMedia Ssrc %d\n", t.MediaSSRC) + out += fmt.Sprintf("\tBase Sequence Number %d\n", t.BaseSequenceNumber) + out += fmt.Sprintf("\tStatus Count %d\n", t.PacketStatusCount) + out += fmt.Sprintf("\tReference Time %d\n", t.ReferenceTime) + out += fmt.Sprintf("\tFeedback Packet Count %d\n", t.FbPktCount) + out += "\tPacketChunks " + for _, chunk := range t.PacketChunks { + out += fmt.Sprintf("%+v ", chunk) + } + out += "\n\tRecvDeltas " + for _, delta := range t.RecvDeltas { + out += fmt.Sprintf("%+v ", delta) + } + out += "\n" + return out +} + +// Marshal encodes the TransportLayerCC in binary +func (t TransportLayerCC) Marshal() ([]byte, error) { + header, err := t.Header.Marshal() + if err != nil { + return nil, err + } + + payload := make([]byte, t.Len()-headerLength) + binary.BigEndian.PutUint32(payload, t.SenderSSRC) + binary.BigEndian.PutUint32(payload[4:], t.MediaSSRC) + binary.BigEndian.PutUint16(payload[baseSequenceNumberOffset:], t.BaseSequenceNumber) + binary.BigEndian.PutUint16(payload[packetStatusCountOffset:], t.PacketStatusCount) + ReferenceTimeAndFbPktCount := appendNBitsToUint32(0, 24, t.ReferenceTime) + ReferenceTimeAndFbPktCount = appendNBitsToUint32(ReferenceTimeAndFbPktCount, 8, uint32(t.FbPktCount)) + binary.BigEndian.PutUint32(payload[referenceTimeOffset:], ReferenceTimeAndFbPktCount) + + for i, chunk := range t.PacketChunks { + b, err := chunk.Marshal() + if err != nil { + return nil, err + } + copy(payload[packetChunkOffset+i*2:], b) + } + + recvDeltaOffset := packetChunkOffset + len(t.PacketChunks)*2 + var i int + for _, delta := range t.RecvDeltas { + b, err := delta.Marshal() + if err == nil { + copy(payload[recvDeltaOffset+i:], b) + i++ + if delta.Type == TypeTCCPacketReceivedLargeDelta { + i++ + } + } + } + + if t.Header.Padding { + payload[len(payload)-1] = uint8(t.Len() - t.packetLen()) + } + + return append(header, payload...), nil +} + +// Unmarshal .. +func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit + if len(rawPacket) < (headerLength + ssrcLength) { + return errPacketTooShort + } + + if err := t.Header.Unmarshal(rawPacket); err != nil { + return err + } + + // https://tools.ietf.org/html/rfc4585#page-33 + // header's length + payload's length + totalLength := 4 * (t.Header.Length + 1) + + if totalLength <= headerLength+packetChunkOffset { + return errPacketTooShort + } + + if len(rawPacket) < int(totalLength) { + return errPacketTooShort + } + + if t.Header.Type != TypeTransportSpecificFeedback || t.Header.Count != FormatTCC { + return errWrongType + } + + t.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + t.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + t.BaseSequenceNumber = binary.BigEndian.Uint16(rawPacket[headerLength+baseSequenceNumberOffset:]) + t.PacketStatusCount = binary.BigEndian.Uint16(rawPacket[headerLength+packetStatusCountOffset:]) + t.ReferenceTime = get24BitsFromBytes(rawPacket[headerLength+referenceTimeOffset : headerLength+referenceTimeOffset+3]) + t.FbPktCount = rawPacket[headerLength+fbPktCountOffset] + + packetStatusPos := uint16(headerLength + packetChunkOffset) + var processedPacketNum uint16 + for processedPacketNum < t.PacketStatusCount { + if packetStatusPos+packetStatusChunkLength >= totalLength { + return errPacketTooShort + } + typ := getNBitsFromByte(rawPacket[packetStatusPos : packetStatusPos+1][0], 0, 1) + var iPacketStatus PacketStatusChunk + switch typ { + case TypeTCCRunLengthChunk: + packetStatus := &RunLengthChunk{Type: typ} + iPacketStatus = packetStatus + err := packetStatus.Unmarshal(rawPacket[packetStatusPos : packetStatusPos+2]) + if err != nil { + return err + } + + packetNumberToProcess := min(t.PacketStatusCount-processedPacketNum, packetStatus.RunLength) + if packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedSmallDelta || + packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedLargeDelta { + for j := uint16(0); j < packetNumberToProcess; j++ { + t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: packetStatus.PacketStatusSymbol}) + } + } + processedPacketNum += packetNumberToProcess + case TypeTCCStatusVectorChunk: + packetStatus := &StatusVectorChunk{Type: typ} + iPacketStatus = packetStatus + err := packetStatus.Unmarshal(rawPacket[packetStatusPos : packetStatusPos+2]) + if err != nil { + return err + } + if packetStatus.SymbolSize == TypeTCCSymbolSizeOneBit { + for j := 0; j < len(packetStatus.SymbolList); j++ { + if packetStatus.SymbolList[j] == TypeTCCPacketReceivedSmallDelta { + t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: TypeTCCPacketReceivedSmallDelta}) + } + } + } + if packetStatus.SymbolSize == TypeTCCSymbolSizeTwoBit { + for j := 0; j < len(packetStatus.SymbolList); j++ { + if packetStatus.SymbolList[j] == TypeTCCPacketReceivedSmallDelta || packetStatus.SymbolList[j] == TypeTCCPacketReceivedLargeDelta { + t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: packetStatus.SymbolList[j]}) + } + } + } + processedPacketNum += uint16(len(packetStatus.SymbolList)) + } + packetStatusPos += packetStatusChunkLength + t.PacketChunks = append(t.PacketChunks, iPacketStatus) + } + + recvDeltasPos := packetStatusPos + for _, delta := range t.RecvDeltas { + if recvDeltasPos >= totalLength { + return errPacketTooShort + } + if delta.Type == TypeTCCPacketReceivedSmallDelta { + err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+1]) + if err != nil { + return err + } + recvDeltasPos++ + } + if delta.Type == TypeTCCPacketReceivedLargeDelta { + err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+2]) + if err != nil { + return err + } + recvDeltasPos += 2 + } + } + + return nil +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (t TransportLayerCC) DestinationSSRC() []uint32 { + return []uint32{t.MediaSSRC} +} + +func min(x, y uint16) uint16 { + if x < y { + return x + } + return y +} diff --git a/vendor/github.com/pion/rtcp/transport_layer_nack.go b/vendor/github.com/pion/rtcp/transport_layer_nack.go new file mode 100644 index 0000000..f7ab803 --- /dev/null +++ b/vendor/github.com/pion/rtcp/transport_layer_nack.go @@ -0,0 +1,174 @@ +package rtcp + +import ( + "encoding/binary" + "fmt" + "math" +) + +// PacketBitmap shouldn't be used like a normal integral, +// so it's type is masked here. Access it with PacketList(). +type PacketBitmap uint16 + +// NackPair is a wire-representation of a collection of +// Lost RTP packets +type NackPair struct { + // ID of lost packets + PacketID uint16 + + // Bitmask of following lost packets + LostPackets PacketBitmap +} + +// The TransportLayerNack packet informs the encoder about the loss of a transport packet +// IETF RFC 4585, Section 6.2.1 +// https://tools.ietf.org/html/rfc4585#section-6.2.1 +type TransportLayerNack struct { + // SSRC of sender + SenderSSRC uint32 + + // SSRC of the media source + MediaSSRC uint32 + + 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) { + if len(sequenceNumbers) == 0 { + return []NackPair{} + } + + nackPair := &NackPair{PacketID: sequenceNumbers[0]} + for i := 1; i < len(sequenceNumbers); i++ { + m := sequenceNumbers[i] + + if m-nackPair.PacketID > 16 { + pairs = append(pairs, *nackPair) + nackPair = &NackPair{PacketID: m} + continue + } + + nackPair.LostPackets |= 1 << (m - nackPair.PacketID - 1) + } + pairs = append(pairs, *nackPair) + return +} + +// Range calls f sequentially for each sequence number covered by n. +// If f returns false, Range stops the iteration. +func (n *NackPair) Range(f func(seqno uint16) bool) { + more := f(n.PacketID) + if !more { + return + } + + b := n.LostPackets + for i := uint16(0); b != 0; i++ { + if (b & (1 << i)) != 0 { + b &^= (1 << i) + more = f(n.PacketID + i + 1) + if !more { + return + } + } + } +} + +// PacketList returns a list of Nack'd packets that's referenced by a NackPair +func (n *NackPair) PacketList() []uint16 { + out := make([]uint16, 0, 17) + n.Range(func(seqno uint16) bool { + out = append(out, seqno) + return true + }) + return out +} + +const ( + tlnLength = 2 + nackOffset = 8 +) + +// Marshal encodes the TransportLayerNack in binary +func (p TransportLayerNack) Marshal() ([]byte, error) { + if len(p.Nacks)+tlnLength > math.MaxUint8 { + return nil, errTooManyReports + } + + rawPacket := make([]byte, nackOffset+(len(p.Nacks)*4)) + binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC) + binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC) + for i := 0; i < len(p.Nacks); i++ { + binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i):], p.Nacks[i].PacketID) + binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i)+2:], uint16(p.Nacks[i].LostPackets)) + } + h := p.Header() + hData, err := h.Marshal() + if err != nil { + return nil, err + } + + return append(hData, rawPacket...), nil +} + +// Unmarshal decodes the TransportLayerNack from binary +func (p *TransportLayerNack) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < (headerLength + ssrcLength) { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + + if len(rawPacket) < (headerLength + int(4*h.Length)) { + return errPacketTooShort + } + + if h.Type != TypeTransportSpecificFeedback || h.Count != FormatTLN { + return errWrongType + } + + p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) + for i := headerLength + nackOffset; i < (headerLength + int(h.Length*4)); i += 4 { + p.Nacks = append(p.Nacks, NackPair{ + binary.BigEndian.Uint16(rawPacket[i:]), + PacketBitmap(binary.BigEndian.Uint16(rawPacket[i+2:])), + }) + } + return nil +} + +func (p *TransportLayerNack) len() int { + return headerLength + nackOffset + (len(p.Nacks) * 4) +} + +// Header returns the Header associated with this packet. +func (p *TransportLayerNack) Header() Header { + return Header{ + Count: FormatTLN, + Type: TypeTransportSpecificFeedback, + Length: uint16((p.len() / 4) - 1), + } +} + +func (p TransportLayerNack) String() string { + out := fmt.Sprintf("TransportLayerNack from %x\n", p.SenderSSRC) + out += fmt.Sprintf("\tMedia Ssrc %x\n", p.MediaSSRC) + out += "\tID\tLostPackets\n" + for _, i := range p.Nacks { + out += fmt.Sprintf("\t%d\t%b\n", i.PacketID, i.LostPackets) + } + return out +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (p *TransportLayerNack) DestinationSSRC() []uint32 { + return []uint32{p.MediaSSRC} +} diff --git a/vendor/github.com/pion/rtcp/util.go b/vendor/github.com/pion/rtcp/util.go new file mode 100644 index 0000000..5702d35 --- /dev/null +++ b/vendor/github.com/pion/rtcp/util.go @@ -0,0 +1,38 @@ +package rtcp + +// getPadding Returns the padding required to make the length a multiple of 4 +func getPadding(len int) int { + if len%4 == 0 { + return 0 + } + return 4 - (len % 4) +} + +// setNBitsOfUint16 will truncate the value to size, left-shift to startIndex position and set +func setNBitsOfUint16(src, size, startIndex, val uint16) (uint16, error) { + if startIndex+size > 16 { + return 0, errInvalidSizeOrStartIndex + } + + // truncate val to size bits + val &= (1 << size) - 1 + + return src | (val << (16 - size - startIndex)), nil +} + +// appendBit32 will left-shift and append n bits of val +func appendNBitsToUint32(src, n, val uint32) uint32 { + return (src << n) | (val & (0xFFFFFFFF >> (32 - n))) +} + +// getNBit get n bits from 1 byte, begin with a position +func getNBitsFromByte(b byte, begin, n uint16) uint16 { + endShift := 8 - (begin + n) + mask := (0xFF >> begin) & uint8(0xFF<<endShift) + return uint16(b&mask) >> endShift +} + +// get24BitFromBytes get 24bits from `[3]byte` slice +func get24BitsFromBytes(b []byte) uint32 { + return uint32(b[0])<<16 + uint32(b[1])<<8 + uint32(b[2]) +} |