24
vendor/github.com/pion/rtcp/.gitignore
generated
vendored
Normal file
24
vendor/github.com/pion/rtcp/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
### JetBrains IDE ###
|
||||
#####################
|
||||
.idea/
|
||||
|
||||
### Emacs Temporary Files ###
|
||||
#############################
|
||||
*~
|
||||
|
||||
### Folders ###
|
||||
###############
|
||||
bin/
|
||||
vendor/
|
||||
node_modules/
|
||||
|
||||
### Files ###
|
||||
#############
|
||||
*.ivf
|
||||
*.ogg
|
||||
tags
|
||||
cover.out
|
||||
*.sw[poe]
|
||||
*.wasm
|
||||
examples/sfu-ws/cert.pem
|
||||
examples/sfu-ws/key.pem
|
||||
89
vendor/github.com/pion/rtcp/.golangci.yml
generated
vendored
Normal file
89
vendor/github.com/pion/rtcp/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
misspell:
|
||||
locale: US
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||
- bodyclose # checks whether HTTP response body is closed successfully
|
||||
- deadcode # Finds unused code
|
||||
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||
- dupl # Tool for code clone detection
|
||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- gci # Gci control golang package import order and make it always deterministic.
|
||||
- gochecknoglobals # Checks that no globals are present in Go code
|
||||
- gochecknoinits # Checks that no init functions are present in Go code
|
||||
- gocognit # Computes and checks the cognitive complexity of functions
|
||||
- goconst # Finds repeated strings that could be replaced by a constant
|
||||
- gocritic # The most opinionated Go source code linter
|
||||
- godox # Tool for detection of FIXME, TODO and other comment keywords
|
||||
- goerr113 # Golang linter to check the errors handling expressions
|
||||
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
|
||||
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
|
||||
- goheader # Checks is file header matches to pattern
|
||||
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
|
||||
- golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
||||
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
|
||||
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||
- gosec # Inspects source code for security problems
|
||||
- gosimple # Linter for Go source code that specializes in simplifying a code
|
||||
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- ineffassign # Detects when assignments to existing variables are not used
|
||||
- misspell # Finds commonly misspelled English words in comments
|
||||
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||
- noctx # noctx finds sending http request without context.Context
|
||||
- scopelint # Scopelint checks for unpinned variables in go programs
|
||||
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||
- structcheck # Finds unused struct fields
|
||||
- stylecheck # Stylecheck is a replacement for golint
|
||||
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
|
||||
- unconvert # Remove unnecessary type conversions
|
||||
- unparam # Reports unused function parameters
|
||||
- unused # Checks Go code for unused constants, variables, functions and types
|
||||
- varcheck # Finds unused global variables and constants
|
||||
- whitespace # Tool for detection of leading and trailing whitespace
|
||||
disable:
|
||||
- funlen # Tool for detection of long functions
|
||||
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||
- godot # Check if comments end in a period
|
||||
- gomnd # An analyzer to detect magic numbers.
|
||||
- lll # Reports long lines
|
||||
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
|
||||
- nestif # Reports deeply nested if statements
|
||||
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
|
||||
- testpackage # linter that makes you use a separate _test package
|
||||
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
# Allow complex tests, better to be self contained
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocognit
|
||||
|
||||
# Allow complex main function in examples
|
||||
- path: examples
|
||||
text: "of func `main` is high"
|
||||
linters:
|
||||
- gocognit
|
||||
|
||||
run:
|
||||
skip-dirs-use-default: false
|
||||
22
vendor/github.com/pion/rtcp/AUTHORS.txt
generated
vendored
Normal file
22
vendor/github.com/pion/rtcp/AUTHORS.txt
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Thank you to everyone that made Pion possible. If you are interested in contributing
|
||||
# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
|
||||
#
|
||||
# This file is auto generated, using git to list all individuals contributors.
|
||||
# see `.github/generate-authors.sh` for the scripting
|
||||
Adam Roach <adam@caffeine.tv>
|
||||
adwpc <adwpc@hotmail.com>
|
||||
aggresss <aggresss@163.com>
|
||||
Atsushi Watanabe <atsushi.w@ieee.org>
|
||||
cnderrauber <zengjie9004@gmail.com>
|
||||
Gabor Pongracz <gabor.pongracz@proemergotech.com>
|
||||
Hugo Arregui <hugo.arregui@gmail.com>
|
||||
Hugo Arregui <hugo@decentraland.org>
|
||||
Juliusz Chroboczek <jch@irif.fr>
|
||||
Kevin Wang <kevmo314@gmail.com>
|
||||
lllf <littlelightlittlefire@gmail.com>
|
||||
Luke Curley <kixelated@gmail.com>
|
||||
Max Hawkins <maxhawkins@gmail.com>
|
||||
Sean DuBois <seaduboi@amazon.com>
|
||||
Sean DuBois <sean@siobud.com>
|
||||
Simone Gotti <simone.gotti@gmail.com>
|
||||
Woodrow Douglass <wdouglass@carnegierobotics.com>
|
||||
21
vendor/github.com/pion/rtcp/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pion/rtcp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
36
vendor/github.com/pion/rtcp/README.md
generated
vendored
Normal file
36
vendor/github.com/pion/rtcp/README.md
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<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:
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
||||
20
vendor/github.com/pion/rtcp/codecov.yml
generated
vendored
Normal file
20
vendor/github.com/pion/rtcp/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# DO NOT EDIT THIS FILE
|
||||
#
|
||||
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||
#
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Allow decreasing 2% of total coverage to avoid noise.
|
||||
threshold: 2%
|
||||
patch:
|
||||
default:
|
||||
target: 70%
|
||||
only_pulls: true
|
||||
|
||||
ignore:
|
||||
- "examples/*"
|
||||
- "examples/**/*"
|
||||
153
vendor/github.com/pion/rtcp/compound_packet.go
generated
vendored
Normal file
153
vendor/github.com/pion/rtcp/compound_packet.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
|
||||
// the underlying protocol (for example UDP).
|
||||
//
|
||||
// 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
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
func (c CompoundPacket) String() string {
|
||||
out := "CompoundPacket\n"
|
||||
for _, p := range c {
|
||||
stringer, canString := p.(fmt.Stringer)
|
||||
if canString {
|
||||
out += stringer.String()
|
||||
} else {
|
||||
out += stringify(p)
|
||||
}
|
||||
}
|
||||
out = strings.TrimSuffix(strings.ReplaceAll(out, "\n", "\n\t"), "\t")
|
||||
return out
|
||||
}
|
||||
40
vendor/github.com/pion/rtcp/doc.go
generated
vendored
Normal file
40
vendor/github.com/pion/rtcp/doc.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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:
|
||||
|
||||
pkts, err := rtcp.Unmarshal(rtcpData)
|
||||
// ...
|
||||
for _, pkt := range pkts {
|
||||
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
|
||||
34
vendor/github.com/pion/rtcp/errors.go
generated
vendored
Normal file
34
vendor/github.com/pion/rtcp/errors.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
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")
|
||||
errInvalidBitrate = errors.New("invalid bitrate")
|
||||
errWrongChunkType = errors.New("rtcp: wrong chunk type")
|
||||
errBadStructMemberType = errors.New("rtcp: struct contains unexpected member type")
|
||||
errBadReadParameter = errors.New("rtcp: cannot read into non-pointer")
|
||||
)
|
||||
643
vendor/github.com/pion/rtcp/extended_report.go
generated
vendored
Normal file
643
vendor/github.com/pion/rtcp/extended_report.go
generated
vendored
Normal file
@@ -0,0 +1,643 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// The ExtendedReport packet is an Implementation of RTCP Extended
|
||||
// Reports defined in RFC 3611. It is used to convey detailed
|
||||
// information about an RTP stream. Each packet contains one or
|
||||
// more report blocks, each of which conveys a different kind of
|
||||
// information.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |V=2|P|reserved | PT=XR=207 | length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// : report blocks :
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type ExtendedReport struct {
|
||||
SenderSSRC uint32 `fmt:"0x%X"`
|
||||
Reports []ReportBlock
|
||||
}
|
||||
|
||||
// ReportBlock represents a single report within an ExtendedReport
|
||||
// packet
|
||||
type ReportBlock interface {
|
||||
DestinationSSRC() []uint32
|
||||
setupBlockHeader()
|
||||
unpackBlockHeader()
|
||||
}
|
||||
|
||||
// TypeSpecificField as described in RFC 3611 section 4.5. In typical
|
||||
// cases, users of ExtendedReports shouldn't need to access this,
|
||||
// and should instead use the corresponding fields in the actual
|
||||
// report blocks themselves.
|
||||
type TypeSpecificField uint8
|
||||
|
||||
// XRHeader defines the common fields that must appear at the start
|
||||
// of each report block. In typical cases, users of ExtendedReports
|
||||
// shouldn't need to access this. For locally-constructed report
|
||||
// blocks, these values will not be accurate until the corresponding
|
||||
// packet is marshaled.
|
||||
type XRHeader struct {
|
||||
BlockType BlockTypeType
|
||||
TypeSpecific TypeSpecificField `fmt:"0x%X"`
|
||||
BlockLength uint16
|
||||
}
|
||||
|
||||
// BlockTypeType specifies the type of report in a report block
|
||||
type BlockTypeType uint8
|
||||
|
||||
// Extended Report block types from RFC 3611.
|
||||
const (
|
||||
LossRLEReportBlockType = 1 // RFC 3611, section 4.1
|
||||
DuplicateRLEReportBlockType = 2 // RFC 3611, section 4.2
|
||||
PacketReceiptTimesReportBlockType = 3 // RFC 3611, section 4.3
|
||||
ReceiverReferenceTimeReportBlockType = 4 // RFC 3611, section 4.4
|
||||
DLRRReportBlockType = 5 // RFC 3611, section 4.5
|
||||
StatisticsSummaryReportBlockType = 6 // RFC 3611, section 4.6
|
||||
VoIPMetricsReportBlockType = 7 // RFC 3611, section 4.7
|
||||
)
|
||||
|
||||
// String converts the Extended report block types into readable strings
|
||||
func (t BlockTypeType) String() string {
|
||||
switch t {
|
||||
case LossRLEReportBlockType:
|
||||
return "LossRLEReportBlockType"
|
||||
case DuplicateRLEReportBlockType:
|
||||
return "DuplicateRLEReportBlockType"
|
||||
case PacketReceiptTimesReportBlockType:
|
||||
return "PacketReceiptTimesReportBlockType"
|
||||
case ReceiverReferenceTimeReportBlockType:
|
||||
return "ReceiverReferenceTimeReportBlockType"
|
||||
case DLRRReportBlockType:
|
||||
return "DLRRReportBlockType"
|
||||
case StatisticsSummaryReportBlockType:
|
||||
return "StatisticsSummaryReportBlockType"
|
||||
case VoIPMetricsReportBlockType:
|
||||
return "VoIPMetricsReportBlockType"
|
||||
}
|
||||
return fmt.Sprintf("invalid value %d", t)
|
||||
}
|
||||
|
||||
// rleReportBlock defines the common structure used by both
|
||||
// Loss RLE report blocks (RFC 3611 §4.1) and Duplicate RLE
|
||||
// report blocks (RFC 3611 §4.2).
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT = 1 or 2 | rsvd. | T | block length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | begin_seq | end_seq |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | chunk 1 | chunk 2 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// : ... :
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | chunk n-1 | chunk n |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type rleReportBlock struct {
|
||||
XRHeader
|
||||
T uint8 `encoding:"omit"`
|
||||
SSRC uint32 `fmt:"0x%X"`
|
||||
BeginSeq uint16
|
||||
EndSeq uint16
|
||||
Chunks []Chunk
|
||||
}
|
||||
|
||||
// Chunk as defined in RFC 3611, section 4.1. These represent information
|
||||
// about packet losses and packet duplication. They have three representations:
|
||||
//
|
||||
// Run Length Chunk:
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |C|R| run length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Bit Vector Chunk:
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |C| bit vector |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Terminating Null Chunk:
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type Chunk uint16
|
||||
|
||||
// LossRLEReportBlock is used to report information about packet
|
||||
// losses, as described in RFC 3611, section 4.1
|
||||
type LossRLEReportBlock rleReportBlock
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *LossRLEReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{b.SSRC}
|
||||
}
|
||||
|
||||
func (b *LossRLEReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = LossRLEReportBlockType
|
||||
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *LossRLEReportBlock) unpackBlockHeader() {
|
||||
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
||||
}
|
||||
|
||||
// DuplicateRLEReportBlock is used to report information about packet
|
||||
// duplication, as described in RFC 3611, section 4.1
|
||||
type DuplicateRLEReportBlock rleReportBlock
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *DuplicateRLEReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{b.SSRC}
|
||||
}
|
||||
|
||||
func (b *DuplicateRLEReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = DuplicateRLEReportBlockType
|
||||
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *DuplicateRLEReportBlock) unpackBlockHeader() {
|
||||
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
||||
}
|
||||
|
||||
// ChunkType enumerates the three kinds of chunks described in RFC 3611 section 4.1.
|
||||
type ChunkType uint8
|
||||
|
||||
// These are the valid values that ChunkType can assume
|
||||
const (
|
||||
RunLengthChunkType = 0
|
||||
BitVectorChunkType = 1
|
||||
TerminatingNullChunkType = 2
|
||||
)
|
||||
|
||||
func (c Chunk) String() string {
|
||||
switch c.Type() {
|
||||
case RunLengthChunkType:
|
||||
runType, _ := c.RunType()
|
||||
return fmt.Sprintf("[RunLength type=%d, length=%d]", runType, c.Value())
|
||||
case BitVectorChunkType:
|
||||
return fmt.Sprintf("[BitVector 0b%015b]", c.Value())
|
||||
case TerminatingNullChunkType:
|
||||
return "[TerminatingNull]"
|
||||
}
|
||||
return fmt.Sprintf("[0x%X]", uint16(c))
|
||||
}
|
||||
|
||||
// Type returns the ChunkType that this Chunk represents
|
||||
func (c Chunk) Type() ChunkType {
|
||||
if c == 0 {
|
||||
return TerminatingNullChunkType
|
||||
}
|
||||
return ChunkType(c >> 15)
|
||||
}
|
||||
|
||||
// RunType returns the RunType that this Chunk represents. It is
|
||||
// only valid if ChunkType is RunLengthChunkType.
|
||||
func (c Chunk) RunType() (uint, error) {
|
||||
if c.Type() != RunLengthChunkType {
|
||||
return 0, errWrongChunkType
|
||||
}
|
||||
return uint((c >> 14) & 0x01), nil
|
||||
}
|
||||
|
||||
// Value returns the value represented in this Chunk
|
||||
func (c Chunk) Value() uint {
|
||||
switch c.Type() {
|
||||
case RunLengthChunkType:
|
||||
return uint(c & 0x3FFF)
|
||||
case BitVectorChunkType:
|
||||
return uint(c & 0x7FFF)
|
||||
case TerminatingNullChunkType:
|
||||
return 0
|
||||
}
|
||||
return uint(c)
|
||||
}
|
||||
|
||||
// PacketReceiptTimesReportBlock represents a Packet Receipt Times
|
||||
// report block, as described in RFC 3611 section 4.3.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=3 | rsvd. | T | block length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | begin_seq | end_seq |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Receipt time of packet begin_seq |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Receipt time of packet (begin_seq + 1) mod 65536 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// : ... :
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Receipt time of packet (end_seq - 1) mod 65536 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type PacketReceiptTimesReportBlock struct {
|
||||
XRHeader
|
||||
T uint8 `encoding:"omit"`
|
||||
SSRC uint32 `fmt:"0x%X"`
|
||||
BeginSeq uint16
|
||||
EndSeq uint16
|
||||
ReceiptTime []uint32
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *PacketReceiptTimesReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{b.SSRC}
|
||||
}
|
||||
|
||||
func (b *PacketReceiptTimesReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = PacketReceiptTimesReportBlockType
|
||||
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *PacketReceiptTimesReportBlock) unpackBlockHeader() {
|
||||
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
||||
}
|
||||
|
||||
// ReceiverReferenceTimeReportBlock encodes a Receiver Reference Time
|
||||
// report block as described in RFC 3611 section 4.4.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=4 | reserved | block length = 2 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | NTP timestamp, most significant word |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | NTP timestamp, least significant word |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type ReceiverReferenceTimeReportBlock struct {
|
||||
XRHeader
|
||||
NTPTimestamp uint64
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *ReceiverReferenceTimeReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{}
|
||||
}
|
||||
|
||||
func (b *ReceiverReferenceTimeReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = ReceiverReferenceTimeReportBlockType
|
||||
b.XRHeader.TypeSpecific = 0
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *ReceiverReferenceTimeReportBlock) unpackBlockHeader() {
|
||||
}
|
||||
|
||||
// DLRRReportBlock encodes a DLRR Report Block as described in
|
||||
// RFC 3611 section 4.5.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=5 | reserved | block length |
|
||||
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
// | SSRC_1 (SSRC of first receiver) | sub-
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
// | last RR (LRR) | 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | delay since last RR (DLRR) |
|
||||
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
// | SSRC_2 (SSRC of second receiver) | sub-
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
// : ... : 2
|
||||
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
type DLRRReportBlock struct {
|
||||
XRHeader
|
||||
Reports []DLRRReport
|
||||
}
|
||||
|
||||
// DLRRReport encodes a single report inside a DLRRReportBlock.
|
||||
type DLRRReport struct {
|
||||
SSRC uint32 `fmt:"0x%X"`
|
||||
LastRR uint32
|
||||
DLRR uint32
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *DLRRReportBlock) DestinationSSRC() []uint32 {
|
||||
ssrc := make([]uint32, len(b.Reports))
|
||||
for i, r := range b.Reports {
|
||||
ssrc[i] = r.SSRC
|
||||
}
|
||||
return ssrc
|
||||
}
|
||||
|
||||
func (b *DLRRReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = DLRRReportBlockType
|
||||
b.XRHeader.TypeSpecific = 0
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *DLRRReportBlock) unpackBlockHeader() {
|
||||
}
|
||||
|
||||
// StatisticsSummaryReportBlock encodes a Statistics Summary Report
|
||||
// Block as described in RFC 3611, section 4.6.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=6 |L|D|J|ToH|rsvd.| block length = 9 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | begin_seq | end_seq |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | lost_packets |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | dup_packets |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | min_jitter |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | max_jitter |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | mean_jitter |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | dev_jitter |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | min_ttl_or_hl | max_ttl_or_hl |mean_ttl_or_hl | dev_ttl_or_hl |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type StatisticsSummaryReportBlock struct {
|
||||
XRHeader
|
||||
LossReports bool `encoding:"omit"`
|
||||
DuplicateReports bool `encoding:"omit"`
|
||||
JitterReports bool `encoding:"omit"`
|
||||
TTLorHopLimit TTLorHopLimitType `encoding:"omit"`
|
||||
SSRC uint32 `fmt:"0x%X"`
|
||||
BeginSeq uint16
|
||||
EndSeq uint16
|
||||
LostPackets uint32
|
||||
DupPackets uint32
|
||||
MinJitter uint32
|
||||
MaxJitter uint32
|
||||
MeanJitter uint32
|
||||
DevJitter uint32
|
||||
MinTTLOrHL uint8
|
||||
MaxTTLOrHL uint8
|
||||
MeanTTLOrHL uint8
|
||||
DevTTLOrHL uint8
|
||||
}
|
||||
|
||||
// TTLorHopLimitType encodes values for the ToH field in
|
||||
// a StatisticsSummaryReportBlock
|
||||
type TTLorHopLimitType uint8
|
||||
|
||||
// Values for TTLorHopLimitType
|
||||
const (
|
||||
ToHMissing = 0
|
||||
ToHIPv4 = 1
|
||||
ToHIPv6 = 2
|
||||
)
|
||||
|
||||
func (t TTLorHopLimitType) String() string {
|
||||
switch t {
|
||||
case ToHMissing:
|
||||
return "[ToH Missing]"
|
||||
case ToHIPv4:
|
||||
return "[ToH = IPv4]"
|
||||
case ToHIPv6:
|
||||
return "[ToH = IPv6]"
|
||||
}
|
||||
return "[ToH Flag is Invalid]"
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *StatisticsSummaryReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{b.SSRC}
|
||||
}
|
||||
|
||||
func (b *StatisticsSummaryReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = StatisticsSummaryReportBlockType
|
||||
b.XRHeader.TypeSpecific = 0x00
|
||||
if b.LossReports {
|
||||
b.XRHeader.TypeSpecific |= 0x80
|
||||
}
|
||||
if b.DuplicateReports {
|
||||
b.XRHeader.TypeSpecific |= 0x40
|
||||
}
|
||||
if b.JitterReports {
|
||||
b.XRHeader.TypeSpecific |= 0x20
|
||||
}
|
||||
b.XRHeader.TypeSpecific |= TypeSpecificField((b.TTLorHopLimit & 0x03) << 3)
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *StatisticsSummaryReportBlock) unpackBlockHeader() {
|
||||
b.LossReports = b.XRHeader.TypeSpecific&0x80 != 0
|
||||
b.DuplicateReports = b.XRHeader.TypeSpecific&0x40 != 0
|
||||
b.JitterReports = b.XRHeader.TypeSpecific&0x20 != 0
|
||||
b.TTLorHopLimit = TTLorHopLimitType((b.XRHeader.TypeSpecific & 0x18) >> 3)
|
||||
}
|
||||
|
||||
// VoIPMetricsReportBlock encodes a VoIP Metrics Report Block as described
|
||||
// in RFC 3611, section 4.7.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=7 | reserved | block length = 8 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | loss rate | discard rate | burst density | gap density |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | burst duration | gap duration |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | round trip delay | end system delay |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | signal level | noise level | RERL | Gmin |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | R factor | ext. R factor | MOS-LQ | MOS-CQ |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | RX config | reserved | JB nominal |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | JB maximum | JB abs max |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type VoIPMetricsReportBlock struct {
|
||||
XRHeader
|
||||
SSRC uint32 `fmt:"0x%X"`
|
||||
LossRate uint8
|
||||
DiscardRate uint8
|
||||
BurstDensity uint8
|
||||
GapDensity uint8
|
||||
BurstDuration uint16
|
||||
GapDuration uint16
|
||||
RoundTripDelay uint16
|
||||
EndSystemDelay uint16
|
||||
SignalLevel uint8
|
||||
NoiseLevel uint8
|
||||
RERL uint8
|
||||
Gmin uint8
|
||||
RFactor uint8
|
||||
ExtRFactor uint8
|
||||
MOSLQ uint8
|
||||
MOSCQ uint8
|
||||
RXConfig uint8
|
||||
_ uint8
|
||||
JBNominal uint16
|
||||
JBMaximum uint16
|
||||
JBAbsMax uint16
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *VoIPMetricsReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{b.SSRC}
|
||||
}
|
||||
|
||||
func (b *VoIPMetricsReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockType = VoIPMetricsReportBlockType
|
||||
b.XRHeader.TypeSpecific = 0
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *VoIPMetricsReportBlock) unpackBlockHeader() {
|
||||
}
|
||||
|
||||
// UnknownReportBlock is used to store bytes for any report block
|
||||
// that has an unknown Report Block Type.
|
||||
type UnknownReportBlock struct {
|
||||
XRHeader
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
||||
func (b *UnknownReportBlock) DestinationSSRC() []uint32 {
|
||||
return []uint32{}
|
||||
}
|
||||
|
||||
func (b *UnknownReportBlock) setupBlockHeader() {
|
||||
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
||||
}
|
||||
|
||||
func (b *UnknownReportBlock) unpackBlockHeader() {
|
||||
}
|
||||
|
||||
// Marshal encodes the ExtendedReport in binary
|
||||
func (x ExtendedReport) Marshal() ([]byte, error) {
|
||||
for _, p := range x.Reports {
|
||||
p.setupBlockHeader()
|
||||
}
|
||||
|
||||
length := wireSize(x)
|
||||
|
||||
// RTCP Header
|
||||
header := Header{
|
||||
Type: TypeExtendedReport,
|
||||
Length: uint16(length / 4),
|
||||
}
|
||||
headerBuffer, err := header.Marshal()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
length += len(headerBuffer)
|
||||
|
||||
rawPacket := make([]byte, length)
|
||||
buffer := packetBuffer{bytes: rawPacket}
|
||||
|
||||
err = buffer.write(headerBuffer)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
err = buffer.write(x)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return rawPacket, nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes the ExtendedReport from binary
|
||||
func (x *ExtendedReport) Unmarshal(b []byte) error {
|
||||
var header Header
|
||||
if err := header.Unmarshal(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if header.Type != TypeExtendedReport {
|
||||
return errWrongType
|
||||
}
|
||||
|
||||
buffer := packetBuffer{bytes: b[headerLength:]}
|
||||
err := buffer.read(&x.SenderSSRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for len(buffer.bytes) > 0 {
|
||||
var block ReportBlock
|
||||
|
||||
headerBuffer := buffer
|
||||
xrHeader := XRHeader{}
|
||||
err = headerBuffer.read(&xrHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch xrHeader.BlockType {
|
||||
case LossRLEReportBlockType:
|
||||
block = new(LossRLEReportBlock)
|
||||
case DuplicateRLEReportBlockType:
|
||||
block = new(DuplicateRLEReportBlock)
|
||||
case PacketReceiptTimesReportBlockType:
|
||||
block = new(PacketReceiptTimesReportBlock)
|
||||
case ReceiverReferenceTimeReportBlockType:
|
||||
block = new(ReceiverReferenceTimeReportBlock)
|
||||
case DLRRReportBlockType:
|
||||
block = new(DLRRReportBlock)
|
||||
case StatisticsSummaryReportBlockType:
|
||||
block = new(StatisticsSummaryReportBlock)
|
||||
case VoIPMetricsReportBlockType:
|
||||
block = new(VoIPMetricsReportBlock)
|
||||
default:
|
||||
block = new(UnknownReportBlock)
|
||||
}
|
||||
|
||||
// We need to limit the amount of data available to
|
||||
// this block to the actual length of the block
|
||||
blockLength := (int(xrHeader.BlockLength) + 1) * 4
|
||||
blockBuffer := buffer.split(blockLength)
|
||||
err = blockBuffer.read(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
block.unpackBlockHeader()
|
||||
x.Reports = append(x.Reports, block)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||
func (x *ExtendedReport) DestinationSSRC() []uint32 {
|
||||
ssrc := make([]uint32, 0)
|
||||
for _, p := range x.Reports {
|
||||
ssrc = append(ssrc, p.DestinationSSRC()...)
|
||||
}
|
||||
return ssrc
|
||||
}
|
||||
|
||||
func (x *ExtendedReport) String() string {
|
||||
return stringify(x)
|
||||
}
|
||||
107
vendor/github.com/pion/rtcp/full_intra_request.go
generated
vendored
Normal file
107
vendor/github.com/pion/rtcp/full_intra_request.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
||||
51
vendor/github.com/pion/rtcp/fuzz.go
generated
vendored
Normal file
51
vendor/github.com/pion/rtcp/fuzz.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
||||
155
vendor/github.com/pion/rtcp/goodbye.go
generated
vendored
Normal file
155
vendor/github.com/pion/rtcp/goodbye.go
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (g Goodbye) String() string {
|
||||
out := "Goodbye\n"
|
||||
for i, s := range g.Sources {
|
||||
out += fmt.Sprintf("\tSource %d: %x\n", i, s)
|
||||
}
|
||||
out += fmt.Sprintf("\tReason: %s\n", g.Reason)
|
||||
|
||||
return out
|
||||
}
|
||||
143
vendor/github.com/pion/rtcp/header.go
generated
vendored
Normal file
143
vendor/github.com/pion/rtcp/header.go
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
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
|
||||
TypeExtendedReport PacketType = 207 // RFC 3611
|
||||
|
||||
)
|
||||
|
||||
// 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"
|
||||
case TypeExtendedReport:
|
||||
return "XR"
|
||||
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
|
||||
}
|
||||
118
vendor/github.com/pion/rtcp/packet.go
generated
vendored
Normal file
118
vendor/github.com/pion/rtcp/packet.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
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)
|
||||
}
|
||||
|
||||
case TypeExtendedReport:
|
||||
packet = new(ExtendedReport)
|
||||
|
||||
default:
|
||||
packet = new(RawPacket)
|
||||
}
|
||||
|
||||
err = packet.Unmarshal(inPacket)
|
||||
|
||||
return packet, bytesprocessed, err
|
||||
}
|
||||
256
vendor/github.com/pion/rtcp/packet_buffer.go
generated
vendored
Normal file
256
vendor/github.com/pion/rtcp/packet_buffer.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// These functions implement an introspective structure
|
||||
// serializer/deserializer, designed to allow RTCP packet
|
||||
// Structs to be self-describing. They currently work with
|
||||
// fields of type uint8, uint16, uint32, and uint64 (and
|
||||
// types derived from them).
|
||||
//
|
||||
// - Unexported fields will take up space in the encoded
|
||||
// array, but wil be set to zero when written, and ignore
|
||||
// when read.
|
||||
//
|
||||
// - Fields that are marked with the tag `encoding:"omit"`
|
||||
// will be ignored when reading and writing data.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type Example struct {
|
||||
// A uint32
|
||||
// B bool `encoding:"omit"`
|
||||
// _ uint64
|
||||
// C uint16
|
||||
// }
|
||||
//
|
||||
// "A" will be encoded as four bytes, in network order. "B"
|
||||
// will not be encoded at all. The anonymous uint64 will
|
||||
// encode as 8 bytes of value "0", followed by two bytes
|
||||
// encoding "C" in network order.
|
||||
|
||||
type packetBuffer struct {
|
||||
bytes []byte
|
||||
}
|
||||
|
||||
const omit = "omit"
|
||||
|
||||
// Writes the structure passed to into the buffer that
|
||||
// PacketBuffer is initialized with. This function will
|
||||
// modify the PacketBuffer.bytes slice to exclude those
|
||||
// bytes that have been written into.
|
||||
//
|
||||
func (b *packetBuffer) write(v interface{}) error { //nolint:gocognit
|
||||
value := reflect.ValueOf(v)
|
||||
|
||||
// Indirect is safe to call on non-pointers, and
|
||||
// will simply return the same value in such cases
|
||||
value = reflect.Indirect(value)
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Uint8:
|
||||
if len(b.bytes) < 1 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
if value.CanInterface() {
|
||||
b.bytes[0] = byte(value.Uint())
|
||||
}
|
||||
b.bytes = b.bytes[1:]
|
||||
case reflect.Uint16:
|
||||
if len(b.bytes) < 2 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
if value.CanInterface() {
|
||||
binary.BigEndian.PutUint16(b.bytes, uint16(value.Uint()))
|
||||
}
|
||||
b.bytes = b.bytes[2:]
|
||||
case reflect.Uint32:
|
||||
if len(b.bytes) < 4 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
if value.CanInterface() {
|
||||
binary.BigEndian.PutUint32(b.bytes, uint32(value.Uint()))
|
||||
}
|
||||
b.bytes = b.bytes[4:]
|
||||
case reflect.Uint64:
|
||||
if len(b.bytes) < 8 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
if value.CanInterface() {
|
||||
binary.BigEndian.PutUint64(b.bytes, value.Uint())
|
||||
}
|
||||
b.bytes = b.bytes[8:]
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if value.Index(i).CanInterface() {
|
||||
if err := b.write(value.Index(i).Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
b.bytes = b.bytes[value.Index(i).Type().Size():]
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
encoding := value.Type().Field(i).Tag.Get("encoding")
|
||||
if encoding == omit {
|
||||
continue
|
||||
}
|
||||
if value.Field(i).CanInterface() {
|
||||
if err := b.write(value.Field(i).Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
advance := int(value.Field(i).Type().Size())
|
||||
if len(b.bytes) < advance {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
b.bytes = b.bytes[advance:]
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errBadStructMemberType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reads bytes from the buffer as necessary to populate
|
||||
// the structure passed as a parameter. This function will
|
||||
// modify the PacketBuffer.bytes slice to exclude those
|
||||
// bytes that have already been read.
|
||||
func (b *packetBuffer) read(v interface{}) error { //nolint:gocognit
|
||||
ptr := reflect.ValueOf(v)
|
||||
if ptr.Kind() != reflect.Ptr {
|
||||
return errBadReadParameter
|
||||
}
|
||||
value := reflect.Indirect(ptr)
|
||||
|
||||
// If this is an interface, we need to make it concrete before using it
|
||||
if value.Kind() == reflect.Interface {
|
||||
value = reflect.ValueOf(value.Interface())
|
||||
}
|
||||
value = reflect.Indirect(value)
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Uint8:
|
||||
if len(b.bytes) < 1 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
value.SetUint(uint64(b.bytes[0]))
|
||||
b.bytes = b.bytes[1:]
|
||||
|
||||
case reflect.Uint16:
|
||||
if len(b.bytes) < 2 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
value.SetUint(uint64(binary.BigEndian.Uint16(b.bytes)))
|
||||
b.bytes = b.bytes[2:]
|
||||
|
||||
case reflect.Uint32:
|
||||
if len(b.bytes) < 4 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
value.SetUint(uint64(binary.BigEndian.Uint32(b.bytes)))
|
||||
b.bytes = b.bytes[4:]
|
||||
|
||||
case reflect.Uint64:
|
||||
if len(b.bytes) < 8 {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
value.SetUint(binary.BigEndian.Uint64(b.bytes))
|
||||
b.bytes = b.bytes[8:]
|
||||
|
||||
case reflect.Slice:
|
||||
// If we encounter a slice, we consume the rest of the data
|
||||
// in the buffer and load it into the slice.
|
||||
for len(b.bytes) > 0 {
|
||||
newElementPtr := reflect.New(value.Type().Elem())
|
||||
if err := b.read(newElementPtr.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
if value.CanSet() {
|
||||
value.Set(reflect.Append(value, reflect.Indirect(newElementPtr)))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
encoding := value.Type().Field(i).Tag.Get("encoding")
|
||||
if encoding == omit {
|
||||
continue
|
||||
}
|
||||
if value.Field(i).CanInterface() {
|
||||
field := value.Field(i)
|
||||
newFieldPtr := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // This is the only way to get a typed pointer to a structure's field
|
||||
if err := b.read(newFieldPtr.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
advance := int(value.Field(i).Type().Size())
|
||||
if len(b.bytes) < advance {
|
||||
return errWrongMarshalSize
|
||||
}
|
||||
b.bytes = b.bytes[advance:]
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return errBadStructMemberType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Consumes `size` bytes and returns them as an
|
||||
// independent PacketBuffer
|
||||
func (b *packetBuffer) split(size int) packetBuffer {
|
||||
if size > len(b.bytes) {
|
||||
size = len(b.bytes)
|
||||
}
|
||||
newBuffer := packetBuffer{bytes: b.bytes[:size]}
|
||||
b.bytes = b.bytes[size:]
|
||||
return newBuffer
|
||||
}
|
||||
|
||||
// Returns the size that a structure will encode into.
|
||||
// This fuction doesn't check that Write() will succeed,
|
||||
// and may return unexpectedly large results for those
|
||||
// structures that Write() will fail on
|
||||
func wireSize(v interface{}) int {
|
||||
value := reflect.ValueOf(v)
|
||||
// Indirect is safe to call on non-pointers, and
|
||||
// will simply return the same value in such cases
|
||||
value = reflect.Indirect(value)
|
||||
size := int(0)
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if value.Index(i).CanInterface() {
|
||||
size += wireSize(value.Index(i).Interface())
|
||||
} else {
|
||||
size += int(value.Index(i).Type().Size())
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
encoding := value.Type().Field(i).Tag.Get("encoding")
|
||||
if encoding == omit {
|
||||
continue
|
||||
}
|
||||
if value.Field(i).CanInterface() {
|
||||
size += wireSize(value.Field(i).Interface())
|
||||
} else {
|
||||
size += int(value.Field(i).Type().Size())
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
size = int(value.Type().Size())
|
||||
}
|
||||
return size
|
||||
}
|
||||
103
vendor/github.com/pion/rtcp/packet_stringifier.go
generated
vendored
Normal file
103
vendor/github.com/pion/rtcp/packet_stringifier.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
/*
|
||||
Converts an RTCP Packet into a human-readable format. The Packets
|
||||
themselves can control the presentation as follows:
|
||||
|
||||
- Fields of a type that have a String() method will be formatted
|
||||
with that String method (which should not emit '\n' characters)
|
||||
|
||||
- Otherwise, fields with a tag containing a "fmt" string will use that
|
||||
format when serializing the value. For example, to format an SSRC
|
||||
value as base 16 insted of base 10:
|
||||
|
||||
type ExamplePacket struct {
|
||||
LocalSSRC uint32 `fmt:"0x%X"`
|
||||
RemotsSSRCs []uint32 `fmt:"%X"`
|
||||
}
|
||||
|
||||
- If no fmt string is present, "%+v" is used by default
|
||||
|
||||
The intention of this stringify() function is to simplify creation
|
||||
of String() methods on new packet types, as it provides a simple
|
||||
baseline implementation that works well in the majority of cases.
|
||||
*/
|
||||
func stringify(p Packet) string {
|
||||
value := reflect.Indirect(reflect.ValueOf(p))
|
||||
return formatField(value.Type().String(), "", p, "")
|
||||
}
|
||||
|
||||
func formatField(name string, format string, f interface{}, indent string) string { //nolint:gocognit
|
||||
out := indent
|
||||
value := reflect.ValueOf(f)
|
||||
|
||||
if !value.IsValid() {
|
||||
return fmt.Sprintf("%s%s: <nil>\n", out, name)
|
||||
}
|
||||
|
||||
isPacket := reflect.TypeOf(f).Implements(reflect.TypeOf((*Packet)(nil)).Elem())
|
||||
|
||||
// Resolve pointers to their underlying values
|
||||
if value.Type().Kind() == reflect.Ptr && !value.IsNil() {
|
||||
underlying := reflect.Indirect(value)
|
||||
if underlying.IsValid() {
|
||||
value = underlying
|
||||
}
|
||||
}
|
||||
|
||||
// If the field type has a custom String method, use that
|
||||
// (unless we're a packet, since we want to avoid recursing
|
||||
// back into this function if the Packet's String() method
|
||||
// uses it)
|
||||
if stringMethod := value.MethodByName("String"); !isPacket && stringMethod.IsValid() {
|
||||
out += fmt.Sprintf("%s: %s\n", name, stringMethod.Call([]reflect.Value{}))
|
||||
return out
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
out += fmt.Sprintf("%s:\n", name)
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
if value.Field(i).CanInterface() {
|
||||
format = value.Type().Field(i).Tag.Get("fmt")
|
||||
if format == "" {
|
||||
format = "%+v"
|
||||
}
|
||||
out += formatField(value.Type().Field(i).Name, format, value.Field(i).Interface(), indent+"\t")
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
childKind := value.Type().Elem().Kind()
|
||||
_, hasStringMethod := value.Type().Elem().MethodByName("String")
|
||||
if hasStringMethod || childKind == reflect.Struct || childKind == reflect.Ptr || childKind == reflect.Interface || childKind == reflect.Slice {
|
||||
out += fmt.Sprintf("%s:\n", name)
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
childName := fmt.Sprint(i)
|
||||
// Since interfaces can hold different types of things, we add the
|
||||
// most specific type name to the name to make it clear what the
|
||||
// subsequent fields represent.
|
||||
if value.Index(i).Kind() == reflect.Interface {
|
||||
childName += fmt.Sprintf(" (%s)", reflect.Indirect(reflect.ValueOf(value.Index(i).Interface())).Type())
|
||||
}
|
||||
if value.Index(i).CanInterface() {
|
||||
out += formatField(childName, format, value.Index(i).Interface(), indent+"\t")
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// If we didn't take care of stringing the value already, we fall through to the
|
||||
// generic case. This will print slices of basic types on a single line.
|
||||
fallthrough
|
||||
default:
|
||||
if value.CanInterface() {
|
||||
out += fmt.Sprintf("%s: "+format+"\n", name, value.Interface())
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
89
vendor/github.com/pion/rtcp/picture_loss_indication.go
generated
vendored
Normal file
89
vendor/github.com/pion/rtcp/picture_loss_indication.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
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
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
86
vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
generated
vendored
Normal file
86
vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
42
vendor/github.com/pion/rtcp/raw_packet.go
generated
vendored
Normal file
42
vendor/github.com/pion/rtcp/raw_packet.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
283
vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
generated
vendored
Normal file
283
vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
package rtcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 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 float32
|
||||
|
||||
// SSRC entries which this packet applies to
|
||||
SSRCs []uint32
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const bitratemax = 0x3FFFFp+63
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|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))
|
||||
|
||||
exp := 0
|
||||
bitrate := p.Bitrate
|
||||
|
||||
if bitrate >= bitratemax {
|
||||
bitrate = bitratemax
|
||||
}
|
||||
|
||||
if bitrate < 0 {
|
||||
return 0, errInvalidBitrate
|
||||
}
|
||||
|
||||
for bitrate >= (1 << 18) {
|
||||
bitrate /= 2.0
|
||||
exp++
|
||||
}
|
||||
|
||||
if exp >= (1 << 6) {
|
||||
return 0, errInvalidBitrate
|
||||
}
|
||||
|
||||
mantissa := uint(math.Floor(float64(bitrate)))
|
||||
|
||||
// We can't quite use the binary package because
|
||||
// a) it's a uint24 and b) the exponent is only 6-bits
|
||||
// Just trust me; this is big-endian encoding.
|
||||
buf[17] = byte(exp<<2) | byte(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) {
|
||||
const mantissamax = 0x7FFFFF
|
||||
/*
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|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
|
||||
exp += 127 // bias for IEEE754
|
||||
exp += 23 // IEEE754 biases the decimal to the left, abs-send-time biases it to the right
|
||||
|
||||
// The remaining 2-bits plus the next 16-bits are the mantissa.
|
||||
mantissa := uint32(buf[17]&3)<<16 | uint32(buf[18])<<8 | uint32(buf[19])
|
||||
|
||||
if mantissa != 0 {
|
||||
// ieee754 requires an implicit leading bit
|
||||
for (mantissa & (mantissamax + 1)) == 0 {
|
||||
exp--
|
||||
mantissa *= 2
|
||||
}
|
||||
}
|
||||
|
||||
// bitrate = mantissa * 2^exp
|
||||
p.Bitrate = math.Float32frombits((uint32(exp) << 23) | (mantissa & mantissamax))
|
||||
|
||||
// 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 := 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
|
||||
}
|
||||
191
vendor/github.com/pion/rtcp/receiver_report.go
generated
vendored
Normal file
191
vendor/github.com/pion/rtcp/receiver_report.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
130
vendor/github.com/pion/rtcp/reception_report.go
generated
vendored
Normal file
130
vendor/github.com/pion/rtcp/reception_report.go
generated
vendored
Normal file
@@ -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
|
||||
}
|
||||
27
vendor/github.com/pion/rtcp/renovate.json
generated
vendored
Normal file
27
vendor/github.com/pion/rtcp/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":disableDependencyDashboard"
|
||||
],
|
||||
"postUpdateOptions": [
|
||||
"gomodTidy"
|
||||
],
|
||||
"commitBody": "Generated by renovateBot",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
||||
"automerge": true
|
||||
},
|
||||
{
|
||||
"packagePatterns": ["^golang.org/x/"],
|
||||
"schedule": ["on the first day of the month"]
|
||||
}
|
||||
],
|
||||
"ignorePaths": [
|
||||
".github/workflows/generate-authors.yml",
|
||||
".github/workflows/lint.yaml",
|
||||
".github/workflows/renovate-go-mod-fix.yaml",
|
||||
".github/workflows/test.yaml",
|
||||
".github/workflows/tidy-check.yaml"
|
||||
]
|
||||
}
|
||||
258
vendor/github.com/pion/rtcp/sender_report.go
generated
vendored
Normal file
258
vendor/github.com/pion/rtcp/sender_report.go
generated
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
113
vendor/github.com/pion/rtcp/slice_loss_indication.go
generated
vendored
Normal file
113
vendor/github.com/pion/rtcp/slice_loss_indication.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
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
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
350
vendor/github.com/pion/rtcp/source_description.go
generated
vendored
Normal file
350
vendor/github.com/pion/rtcp/source_description.go
generated
vendored
Normal file
@@ -0,0 +1,350 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
555
vendor/github.com/pion/rtcp/transport_layer_cc.go
generated
vendored
Normal file
555
vendor/github.com/pion/rtcp/transport_layer_cc.go
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
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 (
|
||||
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 {
|
||||
if d.Type == TypeTCCPacketReceivedSmallDelta {
|
||||
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
|
||||
}
|
||||
172
vendor/github.com/pion/rtcp/transport_layer_nack.go
generated
vendored
Normal file
172
vendor/github.com/pion/rtcp/transport_layer_nack.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
38
vendor/github.com/pion/rtcp/util.go
generated
vendored
Normal file
38
vendor/github.com/pion/rtcp/util.go
generated
vendored
Normal file
@@ -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])
|
||||
}
|
||||
Reference in New Issue
Block a user