直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
28
server/vendor/github.com/pion/rtp/.gitignore
generated
vendored
Normal file
28
server/vendor/github.com/pion/rtp/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
### 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
|
||||
wasm_exec.js
|
||||
125
server/vendor/github.com/pion/rtp/.golangci.yml
generated
vendored
Normal file
125
server/vendor/github.com/pion/rtp/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable:
|
||||
- shadow
|
||||
misspell:
|
||||
locale: US
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
forbidigo:
|
||||
forbid:
|
||||
- ^fmt.Print(f|ln)?$
|
||||
- ^log.(Panic|Fatal|Print)(f|ln)?$
|
||||
- ^os.Exit$
|
||||
- ^panic$
|
||||
- ^print(ln)?$
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||
- bidichk # Checks for dangerous unicode character sequences
|
||||
- bodyclose # checks whether HTTP response body is closed successfully
|
||||
- contextcheck # check the function whether use a non-inherited context
|
||||
- decorder # check declaration order and count of types, constants, variables and functions
|
||||
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||
- dupl # Tool for code clone detection
|
||||
- durationcheck # check for two durations multiplied together
|
||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
|
||||
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
|
||||
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- forbidigo # Forbids identifiers
|
||||
- forcetypeassert # finds forced type assertions
|
||||
- gci # Gci control golang package import order and make it always deterministic.
|
||||
- gochecknoglobals # Checks that no globals are present in Go code
|
||||
- gochecknoinits # Checks that no init functions are present in Go code
|
||||
- gocognit # Computes and checks the cognitive complexity of functions
|
||||
- goconst # Finds repeated strings that could be replaced by a constant
|
||||
- gocritic # The most opinionated Go source code linter
|
||||
- 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
|
||||
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
|
||||
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
|
||||
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||
- gosec # Inspects source code for security problems
|
||||
- gosimple # Linter for Go source code that specializes in simplifying a code
|
||||
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- grouper # An analyzer to analyze expression groups.
|
||||
- importas # Enforces consistent import aliases
|
||||
- ineffassign # Detects when assignments to existing variables are not used
|
||||
- misspell # Finds commonly misspelled English words in comments
|
||||
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
|
||||
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
|
||||
- noctx # noctx finds sending http request without context.Context
|
||||
- predeclared # find code that shadows one of Go's predeclared identifiers
|
||||
- revive # golint replacement, finds style mistakes
|
||||
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||
- stylecheck # Stylecheck is a replacement for golint
|
||||
- tagliatelle # Checks the struct tags.
|
||||
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
|
||||
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
|
||||
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
|
||||
- unconvert # Remove unnecessary type conversions
|
||||
- unparam # Reports unused function parameters
|
||||
- unused # Checks Go code for unused constants, variables, functions and types
|
||||
- wastedassign # wastedassign finds wasted assignment statements
|
||||
- whitespace # Tool for detection of leading and trailing whitespace
|
||||
disable:
|
||||
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||
- containedctx # containedctx is a linter that detects struct contained context.Context field
|
||||
- cyclop # checks function and package cyclomatic complexity
|
||||
- exhaustivestruct # Checks if all struct's fields are initialized
|
||||
- funlen # Tool for detection of long functions
|
||||
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||
- godot # Check if comments end in a period
|
||||
- gomnd # An analyzer to detect magic numbers.
|
||||
- ifshort # Checks that your code uses short syntax for if-statements whenever possible
|
||||
- ireturn # Accept Interfaces, Return Concrete Types
|
||||
- lll # Reports long lines
|
||||
- maintidx # maintidx measures the maintainability index of each function.
|
||||
- makezero # Finds slice declarations with non-zero initial length
|
||||
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
|
||||
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||
- nestif # Reports deeply nested if statements
|
||||
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
|
||||
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||
- promlinter # Check Prometheus metrics naming via promlint
|
||||
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
|
||||
- testpackage # linter that makes you use a separate _test package
|
||||
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
|
||||
- varnamelen # checks that the length of a variable's name matches its scope
|
||||
- wrapcheck # Checks that errors returned from external packages are wrapped
|
||||
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude-dirs-use-default: false
|
||||
exclude-rules:
|
||||
# Allow complex tests and examples, better to be self contained
|
||||
- path: (examples|main\.go|_test\.go)
|
||||
linters:
|
||||
- forbidigo
|
||||
- gocognit
|
||||
|
||||
# Allow forbidden identifiers in CLI commands
|
||||
- path: cmd
|
||||
linters:
|
||||
- forbidigo
|
||||
5
server/vendor/github.com/pion/rtp/.goreleaser.yml
generated
vendored
Normal file
5
server/vendor/github.com/pion/rtp/.goreleaser.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
builds:
|
||||
- skip: true
|
||||
9
server/vendor/github.com/pion/rtp/LICENSE
generated
vendored
Normal file
9
server/vendor/github.com/pion/rtp/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
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.
|
||||
35
server/vendor/github.com/pion/rtp/README.md
generated
vendored
Normal file
35
server/vendor/github.com/pion/rtp/README.md
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
Pion RTP
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of RTP</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-rtp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion RTP"></a>
|
||||
<a href="https://sourcegraph.com/github.com/pion/rtp?badge"><img src="https://sourcegraph.com/github.com/pion/rtp/-/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>
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/rtp/test.yaml">
|
||||
<a href="https://pkg.go.dev/github.com/pion/rtp"><img src="https://pkg.go.dev/badge/github.com/pion/rtp.svg" alt="Go Reference"></a>
|
||||
<a href="https://codecov.io/gh/pion/rtp"><img src="https://codecov.io/gh/pion/rtp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/rtp"><img src="https://goreportcard.com/badge/github.com/pion/rtp" 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>
|
||||
|
||||
### 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 [Slack](https://pion.ly/slack).
|
||||
|
||||
Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
|
||||
|
||||
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
|
||||
106
server/vendor/github.com/pion/rtp/abscapturetimeextension.go
generated
vendored
Normal file
106
server/vendor/github.com/pion/rtp/abscapturetimeextension.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
absCaptureTimeExtensionSize = 8
|
||||
absCaptureTimeExtendedExtensionSize = 16
|
||||
)
|
||||
|
||||
// AbsCaptureTimeExtension is a extension payload format in
|
||||
// http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | len=7 | absolute capture timestamp (bit 0-23) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | absolute capture timestamp (bit 24-55) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ... (56-63) |
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
type AbsCaptureTimeExtension struct {
|
||||
Timestamp uint64
|
||||
EstimatedCaptureClockOffset *int64
|
||||
}
|
||||
|
||||
// Marshal serializes the members to buffer.
|
||||
func (t AbsCaptureTimeExtension) Marshal() ([]byte, error) {
|
||||
if t.EstimatedCaptureClockOffset != nil {
|
||||
buf := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(buf[0:8], t.Timestamp)
|
||||
binary.BigEndian.PutUint64(buf[8:16], uint64(*t.EstimatedCaptureClockOffset))
|
||||
return buf, nil
|
||||
}
|
||||
buf := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf[0:8], t.Timestamp)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the members.
|
||||
func (t *AbsCaptureTimeExtension) Unmarshal(rawData []byte) error {
|
||||
if len(rawData) < absCaptureTimeExtensionSize {
|
||||
return errTooSmall
|
||||
}
|
||||
t.Timestamp = binary.BigEndian.Uint64(rawData[0:8])
|
||||
if len(rawData) >= absCaptureTimeExtendedExtensionSize {
|
||||
offset := int64(binary.BigEndian.Uint64(rawData[8:16]))
|
||||
t.EstimatedCaptureClockOffset = &offset
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CaptureTime produces the estimated time.Time represented by this extension.
|
||||
func (t AbsCaptureTimeExtension) CaptureTime() time.Time {
|
||||
return toTime(t.Timestamp)
|
||||
}
|
||||
|
||||
// EstimatedCaptureClockOffsetDuration produces the estimated time.Duration represented by this extension.
|
||||
func (t AbsCaptureTimeExtension) EstimatedCaptureClockOffsetDuration() *time.Duration {
|
||||
if t.EstimatedCaptureClockOffset == nil {
|
||||
return nil
|
||||
}
|
||||
offset := *t.EstimatedCaptureClockOffset
|
||||
negative := false
|
||||
if offset < 0 {
|
||||
offset = -offset
|
||||
negative = true
|
||||
}
|
||||
duration := time.Duration(offset/(1<<32))*time.Second + time.Duration((offset&0xFFFFFFFF)*1e9/(1<<32))*time.Nanosecond
|
||||
if negative {
|
||||
duration = -duration
|
||||
}
|
||||
return &duration
|
||||
}
|
||||
|
||||
// NewAbsCaptureTimeExtension makes new AbsCaptureTimeExtension from time.Time.
|
||||
func NewAbsCaptureTimeExtension(captureTime time.Time) *AbsCaptureTimeExtension {
|
||||
return &AbsCaptureTimeExtension{
|
||||
Timestamp: toNtpTime(captureTime),
|
||||
}
|
||||
}
|
||||
|
||||
// NewAbsCaptureTimeExtensionWithCaptureClockOffset makes new AbsCaptureTimeExtension from time.Time and a clock offset.
|
||||
func NewAbsCaptureTimeExtensionWithCaptureClockOffset(captureTime time.Time, captureClockOffset time.Duration) *AbsCaptureTimeExtension {
|
||||
ns := captureClockOffset.Nanoseconds()
|
||||
negative := false
|
||||
if ns < 0 {
|
||||
ns = -ns
|
||||
negative = true
|
||||
}
|
||||
lsb := (ns / 1e9) & 0xFFFFFFFF
|
||||
msb := (((ns % 1e9) * (1 << 32)) / 1e9) & 0xFFFFFFFF
|
||||
offset := (lsb << 32) | msb
|
||||
if negative {
|
||||
offset = -offset
|
||||
}
|
||||
return &AbsCaptureTimeExtension{
|
||||
Timestamp: toNtpTime(captureTime),
|
||||
EstimatedCaptureClockOffset: &offset,
|
||||
}
|
||||
}
|
||||
81
server/vendor/github.com/pion/rtp/abssendtimeextension.go
generated
vendored
Normal file
81
server/vendor/github.com/pion/rtp/abssendtimeextension.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
absSendTimeExtensionSize = 3
|
||||
)
|
||||
|
||||
// AbsSendTimeExtension is a extension payload format in
|
||||
// http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
type AbsSendTimeExtension struct {
|
||||
Timestamp uint64
|
||||
}
|
||||
|
||||
// Marshal serializes the members to buffer.
|
||||
func (t AbsSendTimeExtension) Marshal() ([]byte, error) {
|
||||
return []byte{
|
||||
byte(t.Timestamp & 0xFF0000 >> 16),
|
||||
byte(t.Timestamp & 0xFF00 >> 8),
|
||||
byte(t.Timestamp & 0xFF),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the members.
|
||||
func (t *AbsSendTimeExtension) Unmarshal(rawData []byte) error {
|
||||
if len(rawData) < absSendTimeExtensionSize {
|
||||
return errTooSmall
|
||||
}
|
||||
t.Timestamp = uint64(rawData[0])<<16 | uint64(rawData[1])<<8 | uint64(rawData[2])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Estimate absolute send time according to the receive time.
|
||||
// Note that if the transmission delay is larger than 64 seconds, estimated time will be wrong.
|
||||
func (t *AbsSendTimeExtension) Estimate(receive time.Time) time.Time {
|
||||
receiveNTP := toNtpTime(receive)
|
||||
ntp := receiveNTP&0xFFFFFFC000000000 | (t.Timestamp&0xFFFFFF)<<14
|
||||
if receiveNTP < ntp {
|
||||
// Receive time must be always later than send time
|
||||
ntp -= 0x1000000 << 14
|
||||
}
|
||||
|
||||
return toTime(ntp)
|
||||
}
|
||||
|
||||
// NewAbsSendTimeExtension makes new AbsSendTimeExtension from time.Time.
|
||||
func NewAbsSendTimeExtension(sendTime time.Time) *AbsSendTimeExtension {
|
||||
return &AbsSendTimeExtension{
|
||||
Timestamp: toNtpTime(sendTime) >> 14,
|
||||
}
|
||||
}
|
||||
|
||||
func toNtpTime(t time.Time) uint64 {
|
||||
var s uint64
|
||||
var f uint64
|
||||
u := uint64(t.UnixNano())
|
||||
s = u / 1e9
|
||||
s += 0x83AA7E80 // offset in seconds between unix epoch and ntp epoch
|
||||
f = u % 1e9
|
||||
f <<= 32
|
||||
f /= 1e9
|
||||
s <<= 32
|
||||
|
||||
return s | f
|
||||
}
|
||||
|
||||
func toTime(t uint64) time.Time {
|
||||
s := t >> 32
|
||||
f := t & 0xFFFFFFFF
|
||||
f *= 1e9
|
||||
f >>= 32
|
||||
s -= 0x83AA7E80
|
||||
u := s*1e9 + f
|
||||
|
||||
return time.Unix(0, int64(u))
|
||||
}
|
||||
63
server/vendor/github.com/pion/rtp/audiolevelextension.go
generated
vendored
Normal file
63
server/vendor/github.com/pion/rtp/audiolevelextension.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// audioLevelExtensionSize One byte header size
|
||||
audioLevelExtensionSize = 1
|
||||
)
|
||||
|
||||
var errAudioLevelOverflow = errors.New("audio level overflow")
|
||||
|
||||
// AudioLevelExtension is a extension payload format described in
|
||||
// https://tools.ietf.org/html/rfc6464
|
||||
//
|
||||
// Implementation based on:
|
||||
// https://chromium.googlesource.com/external/webrtc/+/e2a017725570ead5946a4ca8235af27470ca0df9/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc#49
|
||||
//
|
||||
// One byte format:
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | len=0 |V| level |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Two byte format:
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | len=1 |V| level | 0 (pad) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type AudioLevelExtension struct {
|
||||
Level uint8
|
||||
Voice bool
|
||||
}
|
||||
|
||||
// Marshal serializes the members to buffer
|
||||
func (a AudioLevelExtension) Marshal() ([]byte, error) {
|
||||
if a.Level > 127 {
|
||||
return nil, errAudioLevelOverflow
|
||||
}
|
||||
voice := uint8(0x00)
|
||||
if a.Voice {
|
||||
voice = 0x80
|
||||
}
|
||||
buf := make([]byte, audioLevelExtensionSize)
|
||||
buf[0] = voice | a.Level
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the members
|
||||
func (a *AudioLevelExtension) Unmarshal(rawData []byte) error {
|
||||
if len(rawData) < audioLevelExtensionSize {
|
||||
return errTooSmall
|
||||
}
|
||||
a.Level = rawData[0] & 0x7F
|
||||
a.Voice = rawData[0]&0x80 != 0
|
||||
return nil
|
||||
}
|
||||
22
server/vendor/github.com/pion/rtp/codecov.yml
generated
vendored
Normal file
22
server/vendor/github.com/pion/rtp/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# DO NOT EDIT THIS FILE
|
||||
#
|
||||
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
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/**/*"
|
||||
85
server/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
generated
vendored
Normal file
85
server/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package obu implements tools for working with the Open Bitstream Unit.
|
||||
package obu
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
sevenLsbBitmask = uint(0b01111111)
|
||||
msbBitmask = uint(0b10000000)
|
||||
)
|
||||
|
||||
// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read
|
||||
var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished")
|
||||
|
||||
// EncodeLEB128 encodes a uint as LEB128
|
||||
func EncodeLEB128(in uint) (out uint) {
|
||||
for {
|
||||
// Copy seven bits from in and discard
|
||||
// what we have copied from in
|
||||
out |= (in & sevenLsbBitmask)
|
||||
in >>= 7
|
||||
|
||||
// If we have more bits to encode set MSB
|
||||
// otherwise we are done
|
||||
if in != 0 {
|
||||
out |= msbBitmask
|
||||
out <<= 8
|
||||
} else {
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeLEB128(in uint) (out uint) {
|
||||
for {
|
||||
// Take 7 LSB from in
|
||||
out |= (in & sevenLsbBitmask)
|
||||
|
||||
// Discard the MSB
|
||||
in >>= 8
|
||||
if in == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
out <<= 7
|
||||
}
|
||||
}
|
||||
|
||||
// ReadLeb128 scans an buffer and decodes a Leb128 value.
|
||||
// If the end of the buffer is reached and all MSB are set
|
||||
// an error is returned
|
||||
func ReadLeb128(in []byte) (uint, uint, error) {
|
||||
var encodedLength uint
|
||||
|
||||
for i := range in {
|
||||
encodedLength |= uint(in[i])
|
||||
|
||||
if in[i]&byte(msbBitmask) == 0 {
|
||||
return decodeLEB128(encodedLength), uint(i + 1), nil
|
||||
}
|
||||
|
||||
// Make more room for next read
|
||||
encodedLength <<= 8
|
||||
}
|
||||
|
||||
return 0, 0, ErrFailedToReadLEB128
|
||||
}
|
||||
|
||||
// WriteToLeb128 writes a uint to a LEB128 encoded byte slice.
|
||||
func WriteToLeb128(in uint) []byte {
|
||||
b := make([]byte, 10)
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = byte(in & 0x7f)
|
||||
in >>= 7
|
||||
if in == 0 {
|
||||
return b[:i+1]
|
||||
}
|
||||
b[i] |= 0x80
|
||||
}
|
||||
|
||||
return b // unreachable
|
||||
}
|
||||
204
server/vendor/github.com/pion/rtp/codecs/av1_packet.go
generated
vendored
Normal file
204
server/vendor/github.com/pion/rtp/codecs/av1_packet.go
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp/codecs/av1/obu"
|
||||
)
|
||||
|
||||
const (
|
||||
zMask = byte(0b10000000)
|
||||
zBitshift = 7
|
||||
|
||||
yMask = byte(0b01000000)
|
||||
yBitshift = 6
|
||||
|
||||
wMask = byte(0b00110000)
|
||||
wBitshift = 4
|
||||
|
||||
nMask = byte(0b00001000)
|
||||
nBitshift = 3
|
||||
|
||||
obuFrameTypeMask = byte(0b01111000)
|
||||
obuFrameTypeBitshift = 3
|
||||
|
||||
obuFameTypeSequenceHeader = 1
|
||||
|
||||
av1PayloaderHeadersize = 1
|
||||
|
||||
leb128Size = 1
|
||||
)
|
||||
|
||||
// AV1Payloader payloads AV1 packets
|
||||
type AV1Payloader struct {
|
||||
sequenceHeader []byte
|
||||
}
|
||||
|
||||
// Payload fragments a AV1 packet across one or more byte arrays
|
||||
// See AV1Packet for description of AV1 Payload Header
|
||||
func (p *AV1Payloader) Payload(mtu uint16, payload []byte) (payloads [][]byte) {
|
||||
payloadDataIndex := 0
|
||||
payloadDataRemaining := len(payload)
|
||||
|
||||
// Payload Data and MTU is non-zero
|
||||
if mtu <= 0 || payloadDataRemaining <= 0 {
|
||||
return payloads
|
||||
}
|
||||
|
||||
// Cache Sequence Header and packetize with next payload
|
||||
frameType := (payload[0] & obuFrameTypeMask) >> obuFrameTypeBitshift
|
||||
if frameType == obuFameTypeSequenceHeader {
|
||||
p.sequenceHeader = payload
|
||||
return
|
||||
}
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
obuCount := byte(1)
|
||||
metadataSize := av1PayloaderHeadersize
|
||||
if len(p.sequenceHeader) != 0 {
|
||||
obuCount++
|
||||
metadataSize += leb128Size + len(p.sequenceHeader)
|
||||
}
|
||||
|
||||
out := make([]byte, min(int(mtu), payloadDataRemaining+metadataSize))
|
||||
outOffset := av1PayloaderHeadersize
|
||||
out[0] = obuCount << wBitshift
|
||||
|
||||
if obuCount == 2 {
|
||||
// This Payload contain the start of a Coded Video Sequence
|
||||
out[0] ^= nMask
|
||||
|
||||
out[1] = byte(obu.EncodeLEB128(uint(len(p.sequenceHeader))))
|
||||
copy(out[2:], p.sequenceHeader)
|
||||
|
||||
outOffset += leb128Size + len(p.sequenceHeader)
|
||||
|
||||
p.sequenceHeader = nil
|
||||
}
|
||||
|
||||
outBufferRemaining := len(out) - outOffset
|
||||
copy(out[outOffset:], payload[payloadDataIndex:payloadDataIndex+outBufferRemaining])
|
||||
payloadDataRemaining -= outBufferRemaining
|
||||
payloadDataIndex += outBufferRemaining
|
||||
|
||||
// Does this Fragment contain an OBU that started in a previous payload
|
||||
if len(payloads) > 0 {
|
||||
out[0] ^= zMask
|
||||
}
|
||||
|
||||
// This OBU will be continued in next Payload
|
||||
if payloadDataRemaining != 0 {
|
||||
out[0] ^= yMask
|
||||
}
|
||||
|
||||
payloads = append(payloads, out)
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// AV1Packet represents a depacketized AV1 RTP Packet
|
||||
/*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |Z|Y| W |N|-|-|-|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
|
||||
type AV1Packet struct {
|
||||
// Z: MUST be set to 1 if the first OBU element is an
|
||||
// OBU fragment that is a continuation of an OBU fragment
|
||||
// from the previous packet, and MUST be set to 0 otherwise.
|
||||
Z bool
|
||||
|
||||
// Y: MUST be set to 1 if the last OBU element is an OBU fragment
|
||||
// that will continue in the next packet, and MUST be set to 0 otherwise.
|
||||
Y bool
|
||||
|
||||
// W: two bit field that describes the number of OBU elements in the packet.
|
||||
// This field MUST be set equal to 0 or equal to the number of OBU elements
|
||||
// contained in the packet. If set to 0, each OBU element MUST be preceded by
|
||||
// a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element
|
||||
// MUST NOT be preceded by a length field. Instead, the length of the last OBU
|
||||
// element contained in the packet can be calculated as follows:
|
||||
// Length of the last OBU element =
|
||||
// length of the RTP payload
|
||||
// - length of aggregation header
|
||||
// - length of previous OBU elements including length fields
|
||||
W byte
|
||||
|
||||
// N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set to 0 otherwise.
|
||||
N bool
|
||||
|
||||
// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
|
||||
// AV1Frame provides the tools to construct a collection of OBUs from a collection of OBU Elements
|
||||
OBUElements [][]byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon
|
||||
func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.Z = ((payload[0] & zMask) >> zBitshift) != 0
|
||||
p.Y = ((payload[0] & yMask) >> yBitshift) != 0
|
||||
p.N = ((payload[0] & nMask) >> nBitshift) != 0
|
||||
p.W = (payload[0] & wMask) >> wBitshift
|
||||
|
||||
if p.Z && p.N {
|
||||
return nil, errIsKeyframeAndFragment
|
||||
}
|
||||
|
||||
if !p.zeroAllocation {
|
||||
obuElements, err := p.parseBody(payload[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.OBUElements = obuElements
|
||||
}
|
||||
|
||||
return payload[1:], nil
|
||||
}
|
||||
|
||||
func (p *AV1Packet) parseBody(payload []byte) ([][]byte, error) {
|
||||
if p.OBUElements != nil {
|
||||
return p.OBUElements, nil
|
||||
}
|
||||
|
||||
obuElements := [][]byte{}
|
||||
|
||||
var obuElementLength, bytesRead uint
|
||||
currentIndex := uint(0)
|
||||
for i := 1; ; i++ {
|
||||
if currentIndex == uint(len(payload)) {
|
||||
break
|
||||
}
|
||||
|
||||
// If W bit is set the last OBU Element will have no length header
|
||||
if byte(i) == p.W {
|
||||
bytesRead = 0
|
||||
obuElementLength = uint(len(payload)) - currentIndex
|
||||
} else {
|
||||
var err error
|
||||
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex += bytesRead
|
||||
if uint(len(payload)) < currentIndex+obuElementLength {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
obuElements = append(obuElements, payload[currentIndex:currentIndex+obuElementLength])
|
||||
currentIndex += obuElementLength
|
||||
}
|
||||
|
||||
return obuElements, nil
|
||||
}
|
||||
5
server/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
5
server/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package codecs implements codec specific RTP payloader/depayloaders
|
||||
package codecs
|
||||
39
server/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
39
server/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// audioDepacketizer is a mixin for audio codec depacketizers
|
||||
type audioDepacketizer struct{}
|
||||
|
||||
func (d *audioDepacketizer) IsPartitionTail(_ bool, _ []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *audioDepacketizer) IsPartitionHead(_ []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// videoDepacketizer is a mixin for video codec depacketizers
|
||||
type videoDepacketizer struct {
|
||||
zeroAllocation bool
|
||||
}
|
||||
|
||||
func (d *videoDepacketizer) IsPartitionTail(marker bool, _ []byte) bool {
|
||||
return marker
|
||||
}
|
||||
|
||||
// SetZeroAllocation enables Zero Allocation mode for the depacketizer
|
||||
// By default the Depacketizers will allocate as they parse. These allocations
|
||||
// are needed for Metadata and other optional values. If you don't need this information
|
||||
// enabling SetZeroAllocation gives you higher performance at a reduced feature set.
|
||||
func (d *videoDepacketizer) SetZeroAllocation(zeroAllocation bool) {
|
||||
d.zeroAllocation = zeroAllocation
|
||||
}
|
||||
17
server/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
17
server/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
errShortPacket = errors.New("packet is not large enough")
|
||||
errNilPacket = errors.New("invalid nil packet")
|
||||
errTooManyPDiff = errors.New("too many PDiff")
|
||||
errTooManySpatialLayers = errors.New("too many spatial layers")
|
||||
errUnhandledNALUType = errors.New("NALU Type is unhandled")
|
||||
|
||||
// AV1 Errors
|
||||
errIsKeyframeAndFragment = errors.New("bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe")
|
||||
)
|
||||
25
server/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
25
server/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// G711Payloader payloads G711 packets
|
||||
type G711Payloader struct{}
|
||||
|
||||
// Payload fragments an G711 packet across one or more byte arrays
|
||||
func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var out [][]byte
|
||||
if payload == nil || mtu == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
for len(payload) > int(mtu) {
|
||||
o := make([]byte, mtu)
|
||||
copy(o, payload[:mtu])
|
||||
payload = payload[mtu:]
|
||||
out = append(out, o)
|
||||
}
|
||||
o := make([]byte, len(payload))
|
||||
copy(o, payload)
|
||||
return append(out, o)
|
||||
}
|
||||
25
server/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
25
server/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// G722Payloader payloads G722 packets
|
||||
type G722Payloader struct{}
|
||||
|
||||
// Payload fragments an G722 packet across one or more byte arrays
|
||||
func (p *G722Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var out [][]byte
|
||||
if payload == nil || mtu == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
for len(payload) > int(mtu) {
|
||||
o := make([]byte, mtu)
|
||||
copy(o, payload[:mtu])
|
||||
payload = payload[mtu:]
|
||||
out = append(out, o)
|
||||
}
|
||||
o := make([]byte, len(payload))
|
||||
copy(o, payload)
|
||||
return append(out, o)
|
||||
}
|
||||
297
server/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
297
server/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// H264Payloader payloads H264 packets
|
||||
type H264Payloader struct {
|
||||
spsNalu, ppsNalu []byte
|
||||
}
|
||||
|
||||
const (
|
||||
stapaNALUType = 24
|
||||
fuaNALUType = 28
|
||||
fubNALUType = 29
|
||||
spsNALUType = 7
|
||||
ppsNALUType = 8
|
||||
audNALUType = 9
|
||||
fillerNALUType = 12
|
||||
|
||||
fuaHeaderSize = 2
|
||||
stapaHeaderSize = 1
|
||||
stapaNALULengthSize = 2
|
||||
|
||||
naluTypeBitmask = 0x1F
|
||||
naluRefIdcBitmask = 0x60
|
||||
fuStartBitmask = 0x80
|
||||
fuEndBitmask = 0x40
|
||||
|
||||
outputStapAHeader = 0x78
|
||||
)
|
||||
|
||||
// nolint:gochecknoglobals
|
||||
var (
|
||||
naluStartCode = []byte{0x00, 0x00, 0x01}
|
||||
annexbNALUStartCode = []byte{0x00, 0x00, 0x00, 0x01}
|
||||
)
|
||||
|
||||
func emitNalus(nals []byte, emit func([]byte)) {
|
||||
start := 0
|
||||
length := len(nals)
|
||||
|
||||
for start < length {
|
||||
end := bytes.Index(nals[start:], annexbNALUStartCode)
|
||||
offset := 4
|
||||
if end == -1 {
|
||||
end = bytes.Index(nals[start:], naluStartCode)
|
||||
offset = 3
|
||||
}
|
||||
if end == -1 {
|
||||
emit(nals[start:])
|
||||
break
|
||||
}
|
||||
|
||||
emit(nals[start : start+end])
|
||||
|
||||
// next NAL start position
|
||||
start += end + offset
|
||||
}
|
||||
}
|
||||
|
||||
// Payload fragments a H264 packet across one or more byte arrays
|
||||
func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var payloads [][]byte
|
||||
if len(payload) == 0 {
|
||||
return payloads
|
||||
}
|
||||
|
||||
emitNalus(payload, func(nalu []byte) {
|
||||
if len(nalu) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
naluType := nalu[0] & naluTypeBitmask
|
||||
naluRefIdc := nalu[0] & naluRefIdcBitmask
|
||||
|
||||
switch {
|
||||
case naluType == audNALUType || naluType == fillerNALUType:
|
||||
return
|
||||
case naluType == spsNALUType:
|
||||
p.spsNalu = nalu
|
||||
return
|
||||
case naluType == ppsNALUType:
|
||||
p.ppsNalu = nalu
|
||||
return
|
||||
case p.spsNalu != nil && p.ppsNalu != nil:
|
||||
// Pack current NALU with SPS and PPS as STAP-A
|
||||
spsLen := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))
|
||||
|
||||
ppsLen := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))
|
||||
|
||||
stapANalu := []byte{outputStapAHeader}
|
||||
stapANalu = append(stapANalu, spsLen...)
|
||||
stapANalu = append(stapANalu, p.spsNalu...)
|
||||
stapANalu = append(stapANalu, ppsLen...)
|
||||
stapANalu = append(stapANalu, p.ppsNalu...)
|
||||
if len(stapANalu) <= int(mtu) {
|
||||
out := make([]byte, len(stapANalu))
|
||||
copy(out, stapANalu)
|
||||
payloads = append(payloads, out)
|
||||
}
|
||||
|
||||
p.spsNalu = nil
|
||||
p.ppsNalu = nil
|
||||
}
|
||||
|
||||
// Single NALU
|
||||
if len(nalu) <= int(mtu) {
|
||||
out := make([]byte, len(nalu))
|
||||
copy(out, nalu)
|
||||
payloads = append(payloads, out)
|
||||
return
|
||||
}
|
||||
|
||||
// FU-A
|
||||
maxFragmentSize := int(mtu) - fuaHeaderSize
|
||||
|
||||
// The FU payload consists of fragments of the payload of the fragmented
|
||||
// NAL unit so that if the fragmentation unit payloads of consecutive
|
||||
// FUs are sequentially concatenated, the payload of the fragmented NAL
|
||||
// unit can be reconstructed. The NAL unit type octet of the fragmented
|
||||
// NAL unit is not included as such in the fragmentation unit payload,
|
||||
// but rather the information of the NAL unit type octet of the
|
||||
// fragmented NAL unit is conveyed in the F and NRI fields of the FU
|
||||
// indicator octet of the fragmentation unit and in the type field of
|
||||
// the FU header. An FU payload MAY have any number of octets and MAY
|
||||
// be empty.
|
||||
|
||||
// According to the RFC, the first octet is skipped due to redundant information
|
||||
naluIndex := 1
|
||||
naluLength := len(nalu) - naluIndex
|
||||
naluRemaining := naluLength
|
||||
|
||||
if min(maxFragmentSize, naluRemaining) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for naluRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, naluRemaining)
|
||||
out := make([]byte, fuaHeaderSize+currentFragmentSize)
|
||||
|
||||
// +---------------+
|
||||
// |0|1|2|3|4|5|6|7|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |F|NRI| Type |
|
||||
// +---------------+
|
||||
out[0] = fuaNALUType
|
||||
out[0] |= naluRefIdc
|
||||
|
||||
// +---------------+
|
||||
// |0|1|2|3|4|5|6|7|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |S|E|R| Type |
|
||||
// +---------------+
|
||||
|
||||
out[1] = naluType
|
||||
if naluRemaining == naluLength {
|
||||
// Set start bit
|
||||
out[1] |= 1 << 7
|
||||
} else if naluRemaining-currentFragmentSize == 0 {
|
||||
// Set end bit
|
||||
out[1] |= 1 << 6
|
||||
}
|
||||
|
||||
copy(out[fuaHeaderSize:], nalu[naluIndex:naluIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
naluRemaining -= currentFragmentSize
|
||||
naluIndex += currentFragmentSize
|
||||
}
|
||||
})
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// H264Packet represents the H264 header that is stored in the payload of an RTP Packet
|
||||
type H264Packet struct {
|
||||
IsAVC bool
|
||||
fuaBuffer []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
func (p *H264Packet) doPackaging(buf, nalu []byte) []byte {
|
||||
if p.IsAVC {
|
||||
buf = binary.BigEndian.AppendUint32(buf, uint32(len(nalu)))
|
||||
buf = append(buf, nalu...)
|
||||
return buf
|
||||
}
|
||||
|
||||
buf = append(buf, annexbNALUStartCode...)
|
||||
buf = append(buf, nalu...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// IsDetectedFinalPacketInSequence returns true of the packet passed in has the
|
||||
// marker bit set indicated the end of a packet sequence
|
||||
func (p *H264Packet) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
|
||||
return rtpPacketMarketBit
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
|
||||
func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if p.zeroAllocation {
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
return p.parseBody(payload)
|
||||
}
|
||||
|
||||
func (p *H264Packet) parseBody(payload []byte) ([]byte, error) {
|
||||
if len(payload) == 0 {
|
||||
return nil, fmt.Errorf("%w: %d <=0", errShortPacket, len(payload))
|
||||
}
|
||||
|
||||
// NALU Types
|
||||
// https://tools.ietf.org/html/rfc6184#section-5.4
|
||||
naluType := payload[0] & naluTypeBitmask
|
||||
switch {
|
||||
case naluType > 0 && naluType < 24:
|
||||
return p.doPackaging(nil, payload), nil
|
||||
|
||||
case naluType == stapaNALUType:
|
||||
currOffset := int(stapaHeaderSize)
|
||||
result := []byte{}
|
||||
for currOffset < len(payload) {
|
||||
naluSize := int(binary.BigEndian.Uint16(payload[currOffset:]))
|
||||
currOffset += stapaNALULengthSize
|
||||
|
||||
if len(payload) < currOffset+naluSize {
|
||||
return nil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)", errShortPacket, naluSize, len(payload)-currOffset)
|
||||
}
|
||||
|
||||
result = p.doPackaging(result, payload[currOffset:currOffset+naluSize])
|
||||
currOffset += naluSize
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case naluType == fuaNALUType:
|
||||
if len(payload) < fuaHeaderSize {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
if p.fuaBuffer == nil {
|
||||
p.fuaBuffer = []byte{}
|
||||
}
|
||||
|
||||
p.fuaBuffer = append(p.fuaBuffer, payload[fuaHeaderSize:]...)
|
||||
|
||||
if payload[1]&fuEndBitmask != 0 {
|
||||
naluRefIdc := payload[0] & naluRefIdcBitmask
|
||||
fragmentedNaluType := payload[1] & naluTypeBitmask
|
||||
|
||||
nalu := append([]byte{}, naluRefIdc|fragmentedNaluType)
|
||||
nalu = append(nalu, p.fuaBuffer...)
|
||||
p.fuaBuffer = nil
|
||||
return p.doPackaging(nil, nalu), nil
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
|
||||
}
|
||||
|
||||
// H264PartitionHeadChecker checks H264 partition head.
|
||||
//
|
||||
// Deprecated: replaced by H264Packet.IsPartitionHead()
|
||||
type H264PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
//
|
||||
// Deprecated: replaced by H264Packet.IsPartitionHead()
|
||||
func (*H264PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&H264Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
func (*H264Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if payload[0]&naluTypeBitmask == fuaNALUType ||
|
||||
payload[0]&naluTypeBitmask == fubNALUType {
|
||||
return payload[1]&fuStartBitmask != 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
828
server/vendor/github.com/pion/rtp/codecs/h265_packet.go
generated
vendored
Normal file
828
server/vendor/github.com/pion/rtp/codecs/h265_packet.go
generated
vendored
Normal file
@@ -0,0 +1,828 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//
|
||||
// Errors
|
||||
//
|
||||
|
||||
var (
|
||||
errH265CorruptedPacket = errors.New("corrupted h265 packet")
|
||||
errInvalidH265PacketType = errors.New("invalid h265 packet type")
|
||||
)
|
||||
|
||||
//
|
||||
// Network Abstraction Unit Header implementation
|
||||
//
|
||||
|
||||
const (
|
||||
// sizeof(uint16)
|
||||
h265NaluHeaderSize = 2
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
h265NaluAggregationPacketType = 48
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
|
||||
h265NaluFragmentationUnitType = 49
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
|
||||
h265NaluPACIPacketType = 50
|
||||
)
|
||||
|
||||
// H265NALUHeader is a H265 NAL Unit Header
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4
|
||||
/*
|
||||
* +---------------+---------------+
|
||||
* |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |F| Type | LayerID | TID |
|
||||
* +-------------+-----------------+
|
||||
**/
|
||||
type H265NALUHeader uint16
|
||||
|
||||
func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
|
||||
return H265NALUHeader((uint16(highByte) << 8) | uint16(lowByte))
|
||||
}
|
||||
|
||||
// F is the forbidden bit, should always be 0.
|
||||
func (h H265NALUHeader) F() bool {
|
||||
return (uint16(h) >> 15) != 0
|
||||
}
|
||||
|
||||
// Type of NAL Unit.
|
||||
func (h H265NALUHeader) Type() uint8 {
|
||||
// 01111110 00000000
|
||||
const mask = 0b01111110 << 8
|
||||
return uint8((uint16(h) & mask) >> (8 + 1))
|
||||
}
|
||||
|
||||
// IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.
|
||||
func (h H265NALUHeader) IsTypeVCLUnit() bool {
|
||||
// Type is coded on 6 bits
|
||||
const msbMask = 0b00100000
|
||||
return (h.Type() & msbMask) == 0
|
||||
}
|
||||
|
||||
// LayerID should always be 0 in non-3D HEVC context.
|
||||
func (h H265NALUHeader) LayerID() uint8 {
|
||||
// 00000001 11111000
|
||||
const mask = (0b00000001 << 8) | 0b11111000
|
||||
return uint8((uint16(h) & mask) >> 3)
|
||||
}
|
||||
|
||||
// TID is the temporal identifier of the NAL unit +1.
|
||||
func (h H265NALUHeader) TID() uint8 {
|
||||
const mask = 0b00000111
|
||||
return uint8(uint16(h) & mask)
|
||||
}
|
||||
|
||||
// IsAggregationPacket returns whether or not the packet is an Aggregation packet.
|
||||
func (h H265NALUHeader) IsAggregationPacket() bool {
|
||||
return h.Type() == h265NaluAggregationPacketType
|
||||
}
|
||||
|
||||
// IsFragmentationUnit returns whether or not the packet is a Fragmentation Unit packet.
|
||||
func (h H265NALUHeader) IsFragmentationUnit() bool {
|
||||
return h.Type() == h265NaluFragmentationUnitType
|
||||
}
|
||||
|
||||
// IsPACIPacket returns whether or not the packet is a PACI packet.
|
||||
func (h H265NALUHeader) IsPACIPacket() bool {
|
||||
return h.Type() == h265NaluPACIPacketType
|
||||
}
|
||||
|
||||
//
|
||||
// Single NAL Unit Packet implementation
|
||||
//
|
||||
|
||||
// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr | DONL (conditional) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | NAL unit payload data |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1
|
||||
type H265SingleNALUnitPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
// donl is a 16-bit field, that may or may not be present.
|
||||
donl *uint16
|
||||
// payload of the fragmentation unit.
|
||||
payload []byte
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265SingleNALUnitPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
|
||||
func (p *H265SingleNALUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if payloadHeader.IsFragmentationUnit() || payloadHeader.IsPACIPacket() || payloadHeader.IsAggregationPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
payload = payload[2:]
|
||||
|
||||
if p.mightNeedDONL {
|
||||
// sizeof(uint16)
|
||||
if len(payload) <= 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
p.donl = &donl
|
||||
payload = payload[2:]
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NALU header of the packet.
|
||||
func (p *H265SingleNALUnitPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// DONL returns the DONL of the packet.
|
||||
func (p *H265SingleNALUnitPacket) DONL() *uint16 {
|
||||
return p.donl
|
||||
}
|
||||
|
||||
// Payload returns the Fragmentation Unit packet payload.
|
||||
func (p *H265SingleNALUnitPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
func (p *H265SingleNALUnitPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Aggregation Packets implementation
|
||||
//
|
||||
|
||||
// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* : DONL (conditional) | NALU size |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | NALU size | |
|
||||
* +-+-+-+-+-+-+-+-+ NAL unit |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationUnitFirst struct {
|
||||
donl *uint16
|
||||
nalUnitSize uint16
|
||||
nalUnit []byte
|
||||
}
|
||||
|
||||
// DONL field, when present, specifies the value of the 16 least
|
||||
// significant bits of the decoding order number of the aggregated NAL
|
||||
// unit.
|
||||
func (u H265AggregationUnitFirst) DONL() *uint16 {
|
||||
return u.donl
|
||||
}
|
||||
|
||||
// NALUSize represents the size, in bytes, of the NalUnit.
|
||||
func (u H265AggregationUnitFirst) NALUSize() uint16 {
|
||||
return u.nalUnitSize
|
||||
}
|
||||
|
||||
// NalUnit payload.
|
||||
func (u H265AggregationUnitFirst) NalUnit() []byte {
|
||||
return u.nalUnit
|
||||
}
|
||||
|
||||
// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* : DOND (cond) | NALU size |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | NAL unit |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationUnit struct {
|
||||
dond *uint8
|
||||
nalUnitSize uint16
|
||||
nalUnit []byte
|
||||
}
|
||||
|
||||
// DOND field plus 1 specifies the difference between
|
||||
// the decoding order number values of the current aggregated NAL unit
|
||||
// and the preceding aggregated NAL unit in the same AP.
|
||||
func (u H265AggregationUnit) DOND() *uint8 {
|
||||
return u.dond
|
||||
}
|
||||
|
||||
// NALUSize represents the size, in bytes, of the NalUnit.
|
||||
func (u H265AggregationUnit) NALUSize() uint16 {
|
||||
return u.nalUnitSize
|
||||
}
|
||||
|
||||
// NalUnit payload.
|
||||
func (u H265AggregationUnit) NalUnit() []byte {
|
||||
return u.nalUnit
|
||||
}
|
||||
|
||||
// H265AggregationPacket represents an Aggregation packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=48) | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
* | |
|
||||
* | two or more aggregation units |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationPacket struct {
|
||||
firstUnit *H265AggregationUnitFirst
|
||||
otherUnits []H265AggregationUnit
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265AggregationPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
|
||||
func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsAggregationPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
// First parse the first aggregation unit
|
||||
payload = payload[2:]
|
||||
firstUnit := &H265AggregationUnitFirst{}
|
||||
|
||||
if p.mightNeedDONL {
|
||||
if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
firstUnit.donl = &donl
|
||||
|
||||
payload = payload[2:]
|
||||
}
|
||||
if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
firstUnit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if len(payload) < int(firstUnit.nalUnitSize) {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
firstUnit.nalUnit = payload[:firstUnit.nalUnitSize]
|
||||
payload = payload[firstUnit.nalUnitSize:]
|
||||
|
||||
// Parse remaining Aggregation Units
|
||||
var units []H265AggregationUnit
|
||||
for {
|
||||
unit := H265AggregationUnit{}
|
||||
|
||||
if p.mightNeedDONL {
|
||||
if len(payload) < 1 {
|
||||
break
|
||||
}
|
||||
|
||||
dond := payload[0]
|
||||
unit.dond = &dond
|
||||
|
||||
payload = payload[1:]
|
||||
}
|
||||
|
||||
if len(payload) < 2 {
|
||||
break
|
||||
}
|
||||
unit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if len(payload) < int(unit.nalUnitSize) {
|
||||
break
|
||||
}
|
||||
|
||||
unit.nalUnit = payload[:unit.nalUnitSize]
|
||||
payload = payload[unit.nalUnitSize:]
|
||||
|
||||
units = append(units, unit)
|
||||
}
|
||||
|
||||
// There need to be **at least** two Aggregation Units (first + another one)
|
||||
if len(units) == 0 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.firstUnit = firstUnit
|
||||
p.otherUnits = units
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FirstUnit returns the first Aggregated Unit of the packet.
|
||||
func (p *H265AggregationPacket) FirstUnit() *H265AggregationUnitFirst {
|
||||
return p.firstUnit
|
||||
}
|
||||
|
||||
// OtherUnits returns the all the other Aggregated Unit of the packet (excluding the first one).
|
||||
func (p *H265AggregationPacket) OtherUnits() []H265AggregationUnit {
|
||||
return p.otherUnits
|
||||
}
|
||||
|
||||
func (p *H265AggregationPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Fragmentation Unit implementation
|
||||
//
|
||||
|
||||
const (
|
||||
// sizeof(uint8)
|
||||
h265FragmentationUnitHeaderSize = 1
|
||||
)
|
||||
|
||||
// H265FragmentationUnitHeader is a H265 FU Header
|
||||
/*
|
||||
* +---------------+
|
||||
* |0|1|2|3|4|5|6|7|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |S|E| FuType |
|
||||
* +---------------+
|
||||
**/
|
||||
type H265FragmentationUnitHeader uint8
|
||||
|
||||
// S represents the start of a fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) S() bool {
|
||||
const mask = 0b10000000
|
||||
return ((h & mask) >> 7) != 0
|
||||
}
|
||||
|
||||
// E represents the end of a fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) E() bool {
|
||||
const mask = 0b01000000
|
||||
return ((h & mask) >> 6) != 0
|
||||
}
|
||||
|
||||
// FuType MUST be equal to the field Type of the fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) FuType() uint8 {
|
||||
const mask = 0b00111111
|
||||
return uint8(h) & mask
|
||||
}
|
||||
|
||||
// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=49) | FU header | DONL (cond) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
||||
* | DONL (cond) | |
|
||||
* |-+-+-+-+-+-+-+-+ |
|
||||
* | FU payload |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
|
||||
type H265FragmentationUnitPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
// fuHeader is the header of the fragmentation unit
|
||||
fuHeader H265FragmentationUnitHeader
|
||||
// donl is a 16-bit field, that may or may not be present.
|
||||
donl *uint16
|
||||
// payload of the fragmentation unit.
|
||||
payload []byte
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265FragmentationUnitPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
|
||||
func (p *H265FragmentationUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize + h265FragmentationUnitHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsFragmentationUnit() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
fuHeader := H265FragmentationUnitHeader(payload[2])
|
||||
payload = payload[3:]
|
||||
|
||||
if fuHeader.S() && p.mightNeedDONL {
|
||||
// sizeof(uint16)
|
||||
if len(payload) <= 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
p.donl = &donl
|
||||
payload = payload[2:]
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
p.fuHeader = fuHeader
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NALU header of the packet.
|
||||
func (p *H265FragmentationUnitPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// FuHeader returns the Fragmentation Unit Header of the packet.
|
||||
func (p *H265FragmentationUnitPacket) FuHeader() H265FragmentationUnitHeader {
|
||||
return p.fuHeader
|
||||
}
|
||||
|
||||
// DONL returns the DONL of the packet.
|
||||
func (p *H265FragmentationUnitPacket) DONL() *uint16 {
|
||||
return p.donl
|
||||
}
|
||||
|
||||
// Payload returns the Fragmentation Unit packet payload.
|
||||
func (p *H265FragmentationUnitPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
func (p *H265FragmentationUnitPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// PACI implementation
|
||||
//
|
||||
|
||||
// H265PACIPacket represents a single H265 PACI packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Payload Header Extension Structure (PHES) |
|
||||
* |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
|
||||
* | |
|
||||
* | PACI payload: NAL unit |
|
||||
* | . . . |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
|
||||
type H265PACIPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
|
||||
// Field which holds value for `A`, `cType`, `PHSsize`, `F0`, `F1`, `F2` and `Y` fields.
|
||||
paciHeaderFields uint16
|
||||
|
||||
// phes is a header extension, of byte length `PHSsize`
|
||||
phes []byte
|
||||
|
||||
// Payload contains NAL units & optional padding
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NAL Unit Header.
|
||||
func (p *H265PACIPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// A copies the F bit of the PACI payload NALU.
|
||||
func (p *H265PACIPacket) A() bool {
|
||||
const mask = 0b10000000 << 8
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// CType copies the Type field of the PACI payload NALU.
|
||||
func (p *H265PACIPacket) CType() uint8 {
|
||||
const mask = 0b01111110 << 8
|
||||
return uint8((p.paciHeaderFields & mask) >> (8 + 1))
|
||||
}
|
||||
|
||||
// PHSsize indicates the size of the PHES field.
|
||||
func (p *H265PACIPacket) PHSsize() uint8 {
|
||||
const mask = (0b00000001 << 8) | 0b11110000
|
||||
return uint8((p.paciHeaderFields & mask) >> 4)
|
||||
}
|
||||
|
||||
// F0 indicates the presence of a Temporal Scalability support extension in the PHES.
|
||||
func (p *H265PACIPacket) F0() bool {
|
||||
const mask = 0b00001000
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// F1 must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) F1() bool {
|
||||
const mask = 0b00000100
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// F2 must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) F2() bool {
|
||||
const mask = 0b00000010
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// Y must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) Y() bool {
|
||||
const mask = 0b00000001
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// PHES contains header extensions. Its size is indicated by PHSsize.
|
||||
func (p *H265PACIPacket) PHES() []byte {
|
||||
return p.phes
|
||||
}
|
||||
|
||||
// Payload is a single NALU or NALU-like struct, not including the first two octets (header).
|
||||
func (p *H265PACIPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
// TSCI returns the Temporal Scalability Control Information extension, if present.
|
||||
func (p *H265PACIPacket) TSCI() *H265TSCI {
|
||||
if !p.F0() || p.PHSsize() < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tsci := H265TSCI((uint32(p.phes[0]) << 16) | (uint32(p.phes[1]) << 8) | uint32(p.phes[0]))
|
||||
return &tsci
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
|
||||
func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize + 2
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsPACIPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
paciHeaderFields := (uint16(payload[2]) << 8) | uint16(payload[3])
|
||||
payload = payload[4:]
|
||||
|
||||
p.paciHeaderFields = paciHeaderFields
|
||||
headerExtensionSize := p.PHSsize()
|
||||
|
||||
if len(payload) < int(headerExtensionSize)+1 {
|
||||
p.paciHeaderFields = 0
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
|
||||
if headerExtensionSize > 0 {
|
||||
p.phes = payload[:headerExtensionSize]
|
||||
}
|
||||
|
||||
payload = payload[headerExtensionSize:]
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *H265PACIPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Temporal Scalability Control Information
|
||||
//
|
||||
|
||||
// H265TSCI is a Temporal Scalability Control Information header extension.
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5
|
||||
type H265TSCI uint32
|
||||
|
||||
// TL0PICIDX see RFC7798 for more details.
|
||||
func (h H265TSCI) TL0PICIDX() uint8 {
|
||||
const m1 = 0xFFFF0000
|
||||
const m2 = 0xFF00
|
||||
return uint8((((h & m1) >> 16) & m2) >> 8)
|
||||
}
|
||||
|
||||
// IrapPicID see RFC7798 for more details.
|
||||
func (h H265TSCI) IrapPicID() uint8 {
|
||||
const m1 = 0xFFFF0000
|
||||
const m2 = 0x00FF
|
||||
return uint8(((h & m1) >> 16) & m2)
|
||||
}
|
||||
|
||||
// S see RFC7798 for more details.
|
||||
func (h H265TSCI) S() bool {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b10000000
|
||||
return (uint8((h&m1)>>8) & m2) != 0
|
||||
}
|
||||
|
||||
// E see RFC7798 for more details.
|
||||
func (h H265TSCI) E() bool {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b01000000
|
||||
return (uint8((h&m1)>>8) & m2) != 0
|
||||
}
|
||||
|
||||
// RES see RFC7798 for more details.
|
||||
func (h H265TSCI) RES() uint8 {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b00111111
|
||||
return uint8((h&m1)>>8) & m2
|
||||
}
|
||||
|
||||
//
|
||||
// H265 Packet interface
|
||||
//
|
||||
|
||||
type isH265Packet interface {
|
||||
isH265Packet()
|
||||
}
|
||||
|
||||
var (
|
||||
_ isH265Packet = (*H265FragmentationUnitPacket)(nil)
|
||||
_ isH265Packet = (*H265PACIPacket)(nil)
|
||||
_ isH265Packet = (*H265SingleNALUnitPacket)(nil)
|
||||
_ isH265Packet = (*H265AggregationPacket)(nil)
|
||||
)
|
||||
|
||||
//
|
||||
// Packet implementation
|
||||
//
|
||||
|
||||
// H265Packet represents a H265 packet, stored in the payload of an RTP packet.
|
||||
type H265Packet struct {
|
||||
packet isH265Packet
|
||||
mightNeedDONL bool
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265Packet) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon
|
||||
func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= h265NaluHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), h265NaluHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
|
||||
switch {
|
||||
case payloadHeader.IsPACIPacket():
|
||||
decoded := &H265PACIPacket{}
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
case payloadHeader.IsFragmentationUnit():
|
||||
decoded := &H265FragmentationUnitPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
case payloadHeader.IsAggregationPacket():
|
||||
decoded := &H265AggregationPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
default:
|
||||
decoded := &H265SingleNALUnitPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Packet returns the populated packet.
|
||||
// Must be casted to one of:
|
||||
// - *H265SingleNALUnitPacket
|
||||
// - *H265FragmentationUnitPacket
|
||||
// - *H265AggregationPacket
|
||||
// - *H265PACIPacket
|
||||
// nolint:golint
|
||||
func (p *H265Packet) Packet() isH265Packet {
|
||||
return p.packet
|
||||
}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
func (*H265Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
if H265NALUHeader(binary.BigEndian.Uint16(payload[0:2])).Type() == h265NaluFragmentationUnitType {
|
||||
return H265FragmentationUnitHeader(payload[2]).S()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
49
server/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
49
server/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// OpusPayloader payloads Opus packets
|
||||
type OpusPayloader struct{}
|
||||
|
||||
// Payload fragments an Opus packet across one or more byte arrays
|
||||
func (p *OpusPayloader) Payload(_ uint16, payload []byte) [][]byte {
|
||||
if payload == nil {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
out := make([]byte, len(payload))
|
||||
copy(out, payload)
|
||||
return [][]byte{out}
|
||||
}
|
||||
|
||||
// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet
|
||||
type OpusPacket struct {
|
||||
Payload []byte
|
||||
|
||||
audioDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon
|
||||
func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
|
||||
if packet == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(packet) == 0 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.Payload = packet
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// OpusPartitionHeadChecker checks Opus partition head.
|
||||
//
|
||||
// Deprecated: replaced by OpusPacket.IsPartitionHead()
|
||||
type OpusPartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the Opus partition.
|
||||
//
|
||||
// Deprecated: replaced by OpusPacket.IsPartitionHead()
|
||||
func (*OpusPartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&OpusPacket{}).IsPartitionHead(packet)
|
||||
}
|
||||
234
server/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
234
server/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// VP8Payloader payloads VP8 packets
|
||||
type VP8Payloader struct {
|
||||
EnablePictureID bool
|
||||
pictureID uint16
|
||||
}
|
||||
|
||||
const (
|
||||
vp8HeaderSize = 1
|
||||
)
|
||||
|
||||
// Payload fragments a VP8 packet across one or more byte arrays
|
||||
func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* https://tools.ietf.org/html/rfc7741#section-4.2
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |X|R|N|S|R| PID | (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* X: |I|L|T|K| RSV | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PictureID | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TL0PICIDX | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* T/K: |TID|Y| KEYIDX | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* S: Start of VP8 partition. SHOULD be set to 1 when the first payload
|
||||
* octet of the RTP packet is the beginning of a new VP8 partition,
|
||||
* and MUST NOT be 1 otherwise. The S bit MUST be set to 1 for the
|
||||
* first packet of each encoded frame.
|
||||
*/
|
||||
|
||||
usingHeaderSize := vp8HeaderSize
|
||||
if p.EnablePictureID {
|
||||
switch {
|
||||
case p.pictureID == 0:
|
||||
case p.pictureID < 128:
|
||||
usingHeaderSize = vp8HeaderSize + 2
|
||||
default:
|
||||
usingHeaderSize = vp8HeaderSize + 3
|
||||
}
|
||||
}
|
||||
|
||||
maxFragmentSize := int(mtu) - usingHeaderSize
|
||||
|
||||
payloadData := payload
|
||||
payloadDataRemaining := len(payload)
|
||||
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
// Make sure the fragment/payload size is correct
|
||||
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
|
||||
return payloads
|
||||
}
|
||||
first := true
|
||||
for payloadDataRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
out := make([]byte, usingHeaderSize+currentFragmentSize)
|
||||
|
||||
if first {
|
||||
out[0] = 0x10
|
||||
first = false
|
||||
}
|
||||
if p.EnablePictureID {
|
||||
switch usingHeaderSize {
|
||||
case vp8HeaderSize:
|
||||
case vp8HeaderSize + 2:
|
||||
out[0] |= 0x80
|
||||
out[1] |= 0x80
|
||||
out[2] |= uint8(p.pictureID & 0x7F)
|
||||
case vp8HeaderSize + 3:
|
||||
out[0] |= 0x80
|
||||
out[1] |= 0x80
|
||||
out[2] |= 0x80 | uint8((p.pictureID>>8)&0x7F)
|
||||
out[3] |= uint8(p.pictureID & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
copy(out[usingHeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
p.pictureID++
|
||||
p.pictureID &= 0x7FFF
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet
|
||||
type VP8Packet struct {
|
||||
// Required Header
|
||||
X uint8 /* extended control bits present */
|
||||
N uint8 /* when set to 1 this frame can be discarded */
|
||||
S uint8 /* start of VP8 partition */
|
||||
PID uint8 /* partition index */
|
||||
|
||||
// Extended control bits
|
||||
I uint8 /* 1 if PictureID is present */
|
||||
L uint8 /* 1 if TL0PICIDX is present */
|
||||
T uint8 /* 1 if TID is present */
|
||||
K uint8 /* 1 if KEYIDX is present */
|
||||
|
||||
// Optional extension
|
||||
PictureID uint16 /* 8 or 16 bits, picture ID */
|
||||
TL0PICIDX uint8 /* 8 bits temporal level zero index */
|
||||
TID uint8 /* 2 bits temporal layer index */
|
||||
Y uint8 /* 1 bit layer sync bit */
|
||||
KEYIDX uint8 /* 5 bits temporal key frame index */
|
||||
|
||||
Payload []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon
|
||||
func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) { //nolint: gocognit
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
}
|
||||
|
||||
payloadLen := len(payload)
|
||||
|
||||
payloadIndex := 0
|
||||
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.X = (payload[payloadIndex] & 0x80) >> 7
|
||||
p.N = (payload[payloadIndex] & 0x20) >> 5
|
||||
p.S = (payload[payloadIndex] & 0x10) >> 4
|
||||
p.PID = payload[payloadIndex] & 0x07
|
||||
|
||||
payloadIndex++
|
||||
|
||||
if p.X == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.I = (payload[payloadIndex] & 0x80) >> 7
|
||||
p.L = (payload[payloadIndex] & 0x40) >> 6
|
||||
p.T = (payload[payloadIndex] & 0x20) >> 5
|
||||
p.K = (payload[payloadIndex] & 0x10) >> 4
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.I = 0
|
||||
p.L = 0
|
||||
p.T = 0
|
||||
p.K = 0
|
||||
}
|
||||
|
||||
if p.I == 1 { // PID present?
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit
|
||||
if payloadIndex+1 >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | uint16(payload[payloadIndex+1])
|
||||
payloadIndex += 2
|
||||
} else {
|
||||
p.PictureID = uint16(payload[payloadIndex])
|
||||
payloadIndex++
|
||||
}
|
||||
} else {
|
||||
p.PictureID = 0
|
||||
}
|
||||
|
||||
if p.L == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.TL0PICIDX = payload[payloadIndex]
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.TL0PICIDX = 0
|
||||
}
|
||||
|
||||
if p.T == 1 || p.K == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
if p.T == 1 {
|
||||
p.TID = payload[payloadIndex] >> 6
|
||||
p.Y = (payload[payloadIndex] >> 5) & 0x1
|
||||
} else {
|
||||
p.TID = 0
|
||||
p.Y = 0
|
||||
}
|
||||
if p.K == 1 {
|
||||
p.KEYIDX = payload[payloadIndex] & 0x1F
|
||||
} else {
|
||||
p.KEYIDX = 0
|
||||
}
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.TID = 0
|
||||
p.Y = 0
|
||||
p.KEYIDX = 0
|
||||
}
|
||||
|
||||
p.Payload = payload[payloadIndex:]
|
||||
return p.Payload, nil
|
||||
}
|
||||
|
||||
// VP8PartitionHeadChecker checks VP8 partition head
|
||||
//
|
||||
// Deprecated: replaced by VP8Packet.IsPartitionHead()
|
||||
type VP8PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP8 partition.
|
||||
//
|
||||
// Deprecated: replaced by VP8Packet.IsPartitionHead()
|
||||
func (*VP8PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&VP8Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP8 partition
|
||||
func (*VP8Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 1 {
|
||||
return false
|
||||
}
|
||||
return (payload[0] & 0x10) != 0
|
||||
}
|
||||
65
server/vendor/github.com/pion/rtp/codecs/vp9/bits.go
generated
vendored
Normal file
65
server/vendor/github.com/pion/rtp/codecs/vp9/bits.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vp9
|
||||
|
||||
import "errors"
|
||||
|
||||
var errNotEnoughBits = errors.New("not enough bits")
|
||||
|
||||
func hasSpace(buf []byte, pos int, n int) error {
|
||||
if n > ((len(buf) * 8) - pos) {
|
||||
return errNotEnoughBits
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFlag(buf []byte, pos *int) (bool, error) {
|
||||
err := hasSpace(buf, *pos, 1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return readFlagUnsafe(buf, pos), nil
|
||||
}
|
||||
|
||||
func readFlagUnsafe(buf []byte, pos *int) bool {
|
||||
b := (buf[*pos>>0x03] >> (7 - (*pos & 0x07))) & 0x01
|
||||
*pos++
|
||||
return b == 1
|
||||
}
|
||||
|
||||
func readBits(buf []byte, pos *int, n int) (uint64, error) {
|
||||
err := hasSpace(buf, *pos, n)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return readBitsUnsafe(buf, pos, n), nil
|
||||
}
|
||||
|
||||
func readBitsUnsafe(buf []byte, pos *int, n int) uint64 {
|
||||
res := 8 - (*pos & 0x07)
|
||||
if n < res {
|
||||
v := uint64((buf[*pos>>0x03] >> (res - n)) & (1<<n - 1))
|
||||
*pos += n
|
||||
return v
|
||||
}
|
||||
|
||||
v := uint64(buf[*pos>>0x03] & (1<<res - 1))
|
||||
*pos += res
|
||||
n -= res
|
||||
|
||||
for n >= 8 {
|
||||
v = (v << 8) | uint64(buf[*pos>>0x03])
|
||||
*pos += 8
|
||||
n -= 8
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
v = (v << n) | uint64(buf[*pos>>0x03]>>(8-n))
|
||||
*pos += n
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
221
server/vendor/github.com/pion/rtp/codecs/vp9/header.go
generated
vendored
Normal file
221
server/vendor/github.com/pion/rtp/codecs/vp9/header.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package vp9 contains a VP9 header parser.
|
||||
package vp9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidFrameMarker = errors.New("invalid frame marker")
|
||||
errWrongFrameSyncByte0 = errors.New("wrong frame_sync_byte_0")
|
||||
errWrongFrameSyncByte1 = errors.New("wrong frame_sync_byte_1")
|
||||
errWrongFrameSyncByte2 = errors.New("wrong frame_sync_byte_2")
|
||||
)
|
||||
|
||||
// HeaderColorConfig is the color_config member of an header.
|
||||
type HeaderColorConfig struct {
|
||||
TenOrTwelveBit bool
|
||||
BitDepth uint8
|
||||
ColorSpace uint8
|
||||
ColorRange bool
|
||||
SubsamplingX bool
|
||||
SubsamplingY bool
|
||||
}
|
||||
|
||||
func (c *HeaderColorConfig) unmarshal(profile uint8, buf []byte, pos *int) error {
|
||||
if profile >= 2 {
|
||||
var err error
|
||||
c.TenOrTwelveBit, err = readFlag(buf, pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.TenOrTwelveBit {
|
||||
c.BitDepth = 12
|
||||
} else {
|
||||
c.BitDepth = 10
|
||||
}
|
||||
} else {
|
||||
c.BitDepth = 8
|
||||
}
|
||||
|
||||
tmp, err := readBits(buf, pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ColorSpace = uint8(tmp)
|
||||
|
||||
if c.ColorSpace != 7 {
|
||||
var err error
|
||||
c.ColorRange, err = readFlag(buf, pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if profile == 1 || profile == 3 {
|
||||
err := hasSpace(buf, *pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SubsamplingX = readFlagUnsafe(buf, pos)
|
||||
c.SubsamplingY = readFlagUnsafe(buf, pos)
|
||||
*pos++
|
||||
} else {
|
||||
c.SubsamplingX = true
|
||||
c.SubsamplingY = true
|
||||
}
|
||||
} else {
|
||||
c.ColorRange = true
|
||||
|
||||
if profile == 1 || profile == 3 {
|
||||
c.SubsamplingX = false
|
||||
c.SubsamplingY = false
|
||||
|
||||
err := hasSpace(buf, *pos, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*pos++
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderFrameSize is the frame_size member of an header.
|
||||
type HeaderFrameSize struct {
|
||||
FrameWidthMinus1 uint16
|
||||
FrameHeightMinus1 uint16
|
||||
}
|
||||
|
||||
func (s *HeaderFrameSize) unmarshal(buf []byte, pos *int) error {
|
||||
err := hasSpace(buf, *pos, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.FrameWidthMinus1 = uint16(readBitsUnsafe(buf, pos, 16))
|
||||
s.FrameHeightMinus1 = uint16(readBitsUnsafe(buf, pos, 16))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Header is a VP9 Frame header.
|
||||
// Specification:
|
||||
// https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
|
||||
type Header struct {
|
||||
Profile uint8
|
||||
ShowExistingFrame bool
|
||||
FrameToShowMapIdx uint8
|
||||
NonKeyFrame bool
|
||||
ShowFrame bool
|
||||
ErrorResilientMode bool
|
||||
ColorConfig *HeaderColorConfig
|
||||
FrameSize *HeaderFrameSize
|
||||
}
|
||||
|
||||
// Unmarshal decodes a Header.
|
||||
func (h *Header) Unmarshal(buf []byte) error {
|
||||
pos := 0
|
||||
|
||||
err := hasSpace(buf, pos, 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameMarker := readBitsUnsafe(buf, &pos, 2)
|
||||
if frameMarker != 2 {
|
||||
return errInvalidFrameMarker
|
||||
}
|
||||
|
||||
profileLowBit := uint8(readBitsUnsafe(buf, &pos, 1))
|
||||
profileHighBit := uint8(readBitsUnsafe(buf, &pos, 1))
|
||||
h.Profile = profileHighBit<<1 + profileLowBit
|
||||
|
||||
if h.Profile == 3 {
|
||||
err = hasSpace(buf, pos, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pos++
|
||||
}
|
||||
|
||||
h.ShowExistingFrame, err = readFlag(buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.ShowExistingFrame {
|
||||
var tmp uint64
|
||||
tmp, err = readBits(buf, &pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.FrameToShowMapIdx = uint8(tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = hasSpace(buf, pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.NonKeyFrame = readFlagUnsafe(buf, &pos)
|
||||
h.ShowFrame = readFlagUnsafe(buf, &pos)
|
||||
h.ErrorResilientMode = readFlagUnsafe(buf, &pos)
|
||||
|
||||
if !h.NonKeyFrame {
|
||||
err := hasSpace(buf, pos, 24)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameSyncByte0 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte0 != 0x49 {
|
||||
return errWrongFrameSyncByte0
|
||||
}
|
||||
|
||||
frameSyncByte1 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte1 != 0x83 {
|
||||
return errWrongFrameSyncByte1
|
||||
}
|
||||
|
||||
frameSyncByte2 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte2 != 0x42 {
|
||||
return errWrongFrameSyncByte2
|
||||
}
|
||||
|
||||
h.ColorConfig = &HeaderColorConfig{}
|
||||
err = h.ColorConfig.unmarshal(h.Profile, buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FrameSize = &HeaderFrameSize{}
|
||||
err = h.FrameSize.unmarshal(buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Width returns the video width.
|
||||
func (h Header) Width() uint16 {
|
||||
if h.FrameSize == nil {
|
||||
return 0
|
||||
}
|
||||
return h.FrameSize.FrameWidthMinus1 + 1
|
||||
}
|
||||
|
||||
// Height returns the video height.
|
||||
func (h Header) Height() uint16 {
|
||||
if h.FrameSize == nil {
|
||||
return 0
|
||||
}
|
||||
return h.FrameSize.FrameHeightMinus1 + 1
|
||||
}
|
||||
511
server/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
511
server/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"github.com/pion/randutil"
|
||||
"github.com/pion/rtp/codecs/vp9"
|
||||
)
|
||||
|
||||
// Use global random generator to properly seed by crypto grade random.
|
||||
var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
|
||||
|
||||
// VP9Payloader payloads VP9 packets
|
||||
type VP9Payloader struct {
|
||||
// whether to use flexible mode or non-flexible mode.
|
||||
FlexibleMode bool
|
||||
|
||||
// InitialPictureIDFn is a function that returns random initial picture ID.
|
||||
InitialPictureIDFn func() uint16
|
||||
|
||||
pictureID uint16
|
||||
initialized bool
|
||||
}
|
||||
|
||||
const (
|
||||
maxSpatialLayers = 5
|
||||
maxVP9RefPics = 3
|
||||
)
|
||||
|
||||
// Payload fragments an VP9 packet across one or more byte arrays
|
||||
func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
if !p.initialized {
|
||||
if p.InitialPictureIDFn == nil {
|
||||
p.InitialPictureIDFn = func() uint16 {
|
||||
return uint16(globalMathRandomGenerator.Intn(0x7FFF))
|
||||
}
|
||||
}
|
||||
p.pictureID = p.InitialPictureIDFn() & 0x7FFF
|
||||
p.initialized = true
|
||||
}
|
||||
|
||||
var payloads [][]byte
|
||||
if p.FlexibleMode {
|
||||
payloads = p.payloadFlexible(mtu, payload)
|
||||
} else {
|
||||
payloads = p.payloadNonFlexible(mtu, payload)
|
||||
}
|
||||
|
||||
p.pictureID++
|
||||
if p.pictureID >= 0x8000 {
|
||||
p.pictureID = 0
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
func (p *VP9Payloader) payloadFlexible(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* Flexible mode (F=1)
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |I|P|L|F|B|E|V|Z| (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* M: | EXTENDED PID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+ -\
|
||||
* P,F: | P_DIFF |N| (CONDITIONALLY REQUIRED) - up to 3 times
|
||||
* +-+-+-+-+-+-+-+-+ -/
|
||||
* V: | SS |
|
||||
* | .. |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
headerSize := 3
|
||||
maxFragmentSize := int(mtu) - headerSize
|
||||
payloadDataRemaining := len(payload)
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
out := make([]byte, headerSize+currentFragmentSize)
|
||||
|
||||
out[0] = 0x90 // F=1, I=1
|
||||
if payloadDataIndex == 0 {
|
||||
out[0] |= 0x08 // B=1
|
||||
}
|
||||
if payloadDataRemaining == currentFragmentSize {
|
||||
out[0] |= 0x04 // E=1
|
||||
}
|
||||
|
||||
out[1] = byte(p.pictureID>>8) | 0x80
|
||||
out[2] = byte(p.pictureID)
|
||||
|
||||
copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
func (p *VP9Payloader) payloadNonFlexible(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* Non-flexible mode (F=0)
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |I|P|L|F|B|E|V|Z| (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* M: | EXTENDED PID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* | TL0PICIDX | (CONDITIONALLY REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* V: | SS |
|
||||
* | .. |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
var h vp9.Header
|
||||
err := h.Unmarshal(payload)
|
||||
if err != nil {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
payloadDataRemaining := len(payload)
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
var headerSize int
|
||||
if !h.NonKeyFrame && payloadDataIndex == 0 {
|
||||
headerSize = 3 + 8
|
||||
} else {
|
||||
headerSize = 3
|
||||
}
|
||||
|
||||
maxFragmentSize := int(mtu) - headerSize
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
if currentFragmentSize <= 0 {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
out := make([]byte, headerSize+currentFragmentSize)
|
||||
|
||||
out[0] = 0x80 | 0x01 // I=1, Z=1
|
||||
|
||||
if h.NonKeyFrame {
|
||||
out[0] |= 0x40 // P=1
|
||||
}
|
||||
if payloadDataIndex == 0 {
|
||||
out[0] |= 0x08 // B=1
|
||||
}
|
||||
if payloadDataRemaining == currentFragmentSize {
|
||||
out[0] |= 0x04 // E=1
|
||||
}
|
||||
|
||||
out[1] = byte(p.pictureID>>8) | 0x80
|
||||
out[2] = byte(p.pictureID)
|
||||
off := 3
|
||||
|
||||
if !h.NonKeyFrame && payloadDataIndex == 0 {
|
||||
out[0] |= 0x02 // V=1
|
||||
out[off] = 0x10 | 0x08 // N_S=0, Y=1, G=1
|
||||
off++
|
||||
|
||||
width := h.Width()
|
||||
out[off] = byte(width >> 8)
|
||||
off++
|
||||
out[off] = byte(width & 0xFF)
|
||||
off++
|
||||
|
||||
height := h.Height()
|
||||
out[off] = byte(height >> 8)
|
||||
off++
|
||||
out[off] = byte(height & 0xFF)
|
||||
off++
|
||||
|
||||
out[off] = 0x01 // N_G=1
|
||||
off++
|
||||
|
||||
out[off] = 1<<4 | 1<<2 // TID=0, U=1, R=1
|
||||
off++
|
||||
|
||||
out[off] = 0x01 // P_DIFF=1
|
||||
}
|
||||
|
||||
copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet
|
||||
type VP9Packet struct {
|
||||
// Required header
|
||||
I bool // PictureID is present
|
||||
P bool // Inter-picture predicted frame
|
||||
L bool // Layer indices is present
|
||||
F bool // Flexible mode
|
||||
B bool // Start of a frame
|
||||
E bool // End of a frame
|
||||
V bool // Scalability structure (SS) data present
|
||||
Z bool // Not a reference frame for upper spatial layers
|
||||
|
||||
// Recommended headers
|
||||
PictureID uint16 // 7 or 16 bits, picture ID
|
||||
|
||||
// Conditionally recommended headers
|
||||
TID uint8 // Temporal layer ID
|
||||
U bool // Switching up point
|
||||
SID uint8 // Spatial layer ID
|
||||
D bool // Inter-layer dependency used
|
||||
|
||||
// Conditionally required headers
|
||||
PDiff []uint8 // Reference index (F=1)
|
||||
TL0PICIDX uint8 // Temporal layer zero index (F=0)
|
||||
|
||||
// Scalability structure headers
|
||||
NS uint8 // N_S + 1 indicates the number of spatial layers present in the VP9 stream
|
||||
Y bool // Each spatial layer's frame resolution present
|
||||
G bool // PG description present flag.
|
||||
NG uint8 // N_G indicates the number of pictures in a Picture Group (PG)
|
||||
Width []uint16
|
||||
Height []uint16
|
||||
PGTID []uint8 // Temporal layer ID of pictures in a Picture Group
|
||||
PGU []bool // Switching up point of pictures in a Picture Group
|
||||
PGPDiff [][]uint8 // Reference indecies of pictures in a Picture Group
|
||||
|
||||
Payload []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon
|
||||
func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
|
||||
if packet == nil {
|
||||
return nil, errNilPacket
|
||||
}
|
||||
if len(packet) < 1 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.I = packet[0]&0x80 != 0
|
||||
p.P = packet[0]&0x40 != 0
|
||||
p.L = packet[0]&0x20 != 0
|
||||
p.F = packet[0]&0x10 != 0
|
||||
p.B = packet[0]&0x08 != 0
|
||||
p.E = packet[0]&0x04 != 0
|
||||
p.V = packet[0]&0x02 != 0
|
||||
p.Z = packet[0]&0x01 != 0
|
||||
|
||||
pos := 1
|
||||
var err error
|
||||
|
||||
if p.I {
|
||||
pos, err = p.parsePictureID(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.L {
|
||||
pos, err = p.parseLayerInfo(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.F && p.P {
|
||||
pos, err = p.parseRefIndices(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.V {
|
||||
pos, err = p.parseSSData(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p.Payload = packet[pos:]
|
||||
return p.Payload, nil
|
||||
}
|
||||
|
||||
// Picture ID:
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | M:0 => picture id is 7 bits.
|
||||
* +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
|
||||
* M: | EXTENDED PID |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.PictureID = uint16(packet[pos] & 0x7F)
|
||||
if packet[pos]&0x80 != 0 {
|
||||
pos++
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
p.PictureID = p.PictureID<<8 | uint16(packet[pos])
|
||||
}
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
func (p *VP9Packet) parseLayerInfo(packet []byte, pos int) (int, error) {
|
||||
pos, err := p.parseLayerInfoCommon(packet, pos)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
}
|
||||
|
||||
if p.F {
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
return p.parseLayerInfoNonFlexibleMode(packet, pos)
|
||||
}
|
||||
|
||||
// Layer indices (flexible mode):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | T |U| S |D|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.TID = packet[pos] >> 5
|
||||
p.U = packet[pos]&0x10 != 0
|
||||
p.SID = (packet[pos] >> 1) & 0x7
|
||||
p.D = packet[pos]&0x01 != 0
|
||||
|
||||
if p.SID >= maxSpatialLayers {
|
||||
return pos, errTooManySpatialLayers
|
||||
}
|
||||
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Layer indices (non-flexible mode):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | T |U| S |D|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* | TL0PICIDX |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.TL0PICIDX = packet[pos]
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Reference indices:
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
|
||||
* P,F: | P_DIFF |N| up to 3 times has to be specified.
|
||||
* +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
|
||||
* current P_DIFF.
|
||||
**/
|
||||
func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) {
|
||||
for {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
p.PDiff = append(p.PDiff, packet[pos]>>1)
|
||||
if packet[pos]&0x01 == 0 {
|
||||
break
|
||||
}
|
||||
if len(p.PDiff) >= maxVP9RefPics {
|
||||
return pos, errTooManyPDiff
|
||||
}
|
||||
pos++
|
||||
}
|
||||
pos++
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Scalability structure (SS):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* V: | N_S |Y|G|-|-|-|
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* Y: | WIDTH | (OPTIONAL) .
|
||||
* + .
|
||||
* | | (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ . N_S + 1 times
|
||||
* | HEIGHT | (OPTIONAL) .
|
||||
* + .
|
||||
* | | (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* G: | N_G | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* N_G: | T |U| R |-|-| (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ -| . N_G times
|
||||
* | P_DIFF | (OPTIONAL) . R times .
|
||||
* +-+-+-+-+-+-+-+-+ -| -|
|
||||
**/
|
||||
func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.NS = packet[pos] >> 5
|
||||
p.Y = packet[pos]&0x10 != 0
|
||||
p.G = (packet[pos]>>1)&0x7 != 0
|
||||
pos++
|
||||
|
||||
NS := p.NS + 1
|
||||
p.NG = 0
|
||||
|
||||
if p.Y {
|
||||
p.Width = make([]uint16, NS)
|
||||
p.Height = make([]uint16, NS)
|
||||
for i := 0; i < int(NS); i++ {
|
||||
if len(packet) <= (pos + 3) {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.Width[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
|
||||
pos += 2
|
||||
p.Height[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
|
||||
pos += 2
|
||||
}
|
||||
}
|
||||
|
||||
if p.G {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.NG = packet[pos]
|
||||
pos++
|
||||
}
|
||||
|
||||
for i := 0; i < int(p.NG); i++ {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.PGTID = append(p.PGTID, packet[pos]>>5)
|
||||
p.PGU = append(p.PGU, packet[pos]&0x10 != 0)
|
||||
R := (packet[pos] >> 2) & 0x3
|
||||
pos++
|
||||
|
||||
p.PGPDiff = append(p.PGPDiff, []uint8{})
|
||||
|
||||
if len(packet) <= (pos + int(R) - 1) {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
for j := 0; j < int(R); j++ {
|
||||
p.PGPDiff[i] = append(p.PGPDiff[i], packet[pos])
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// VP9PartitionHeadChecker checks VP9 partition head.
|
||||
//
|
||||
// Deprecated: replaced by VP9Packet.IsPartitionHead()
|
||||
type VP9PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP9 partition.
|
||||
//
|
||||
// Deprecated: replaced by VP9Packet.IsPartitionHead()
|
||||
func (*VP9PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&VP9Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP9 partition
|
||||
func (*VP9Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 1 {
|
||||
return false
|
||||
}
|
||||
return (payload[0] & 0x08) != 0
|
||||
}
|
||||
20
server/vendor/github.com/pion/rtp/depacketizer.go
generated
vendored
Normal file
20
server/vendor/github.com/pion/rtp/depacketizer.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
// Depacketizer depacketizes a RTP payload, removing any RTP specific data from the payload
|
||||
type Depacketizer interface {
|
||||
// Unmarshal parses the RTP payload and returns media.
|
||||
// Metadata may be stored on the Depacketizer itself
|
||||
Unmarshal(packet []byte) ([]byte, error)
|
||||
|
||||
// Checks if the packet is at the beginning of a partition. This
|
||||
// should return false if the result could not be determined, in
|
||||
// which case the caller will detect timestamp discontinuities.
|
||||
IsPartitionHead(payload []byte) bool
|
||||
|
||||
// Checks if the packet is at the end of a partition. This should
|
||||
// return false if the result could not be determined.
|
||||
IsPartitionTail(marker bool, payload []byte) bool
|
||||
}
|
||||
26
server/vendor/github.com/pion/rtp/error.go
generated
vendored
Normal file
26
server/vendor/github.com/pion/rtp/error.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errHeaderSizeInsufficient = errors.New("RTP header size insufficient")
|
||||
errHeaderSizeInsufficientForExtension = errors.New("RTP header size insufficient for extension")
|
||||
errTooSmall = errors.New("buffer too small")
|
||||
errHeaderExtensionsNotEnabled = errors.New("h.Extension not enabled")
|
||||
errHeaderExtensionNotFound = errors.New("extension not found")
|
||||
|
||||
errRFC8285OneByteHeaderIDRange = errors.New("header extension id must be between 1 and 14 for RFC 5285 one byte extensions")
|
||||
errRFC8285OneByteHeaderSize = errors.New("header extension payload must be 16bytes or less for RFC 5285 one byte extensions")
|
||||
|
||||
errRFC8285TwoByteHeaderIDRange = errors.New("header extension id must be between 1 and 255 for RFC 5285 two byte extensions")
|
||||
errRFC8285TwoByteHeaderSize = errors.New("header extension payload must be 255bytes or less for RFC 5285 two byte extensions")
|
||||
|
||||
errRFC3550HeaderIDRange = errors.New("header extension id must be 0 for non-RFC 5285 extensions")
|
||||
|
||||
errInvalidRTPPadding = errors.New("invalid RTP padding")
|
||||
)
|
||||
353
server/vendor/github.com/pion/rtp/header_extension.go
generated
vendored
Normal file
353
server/vendor/github.com/pion/rtp/header_extension.go
generated
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
headerExtensionProfileOneByte = 0xBEDE
|
||||
headerExtensionProfileTwoByte = 0x1000
|
||||
headerExtensionIDReserved = 0xF
|
||||
)
|
||||
|
||||
// HeaderExtension represents an RTP extension header.
|
||||
type HeaderExtension interface {
|
||||
Set(id uint8, payload []byte) error
|
||||
GetIDs() []uint8
|
||||
Get(id uint8) []byte
|
||||
Del(id uint8) error
|
||||
|
||||
Unmarshal(buf []byte) (int, error)
|
||||
Marshal() ([]byte, error)
|
||||
MarshalTo(buf []byte) (int, error)
|
||||
MarshalSize() int
|
||||
}
|
||||
|
||||
// OneByteHeaderExtension is an RFC8285 one-byte header extension.
|
||||
type OneByteHeaderExtension struct {
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// Set sets the extension payload for the specified ID.
|
||||
func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error {
|
||||
if id < 1 || id > 14 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id)
|
||||
}
|
||||
if len(buf) > 16 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(buf))
|
||||
}
|
||||
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n] >> 4
|
||||
payloadLen := int(e.payload[n]&^0xF0 + 1)
|
||||
n++
|
||||
|
||||
if extid == id {
|
||||
e.payload = append(e.payload[:n+1], append(buf, e.payload[n+1+payloadLen:]...)...)
|
||||
return nil
|
||||
}
|
||||
n += payloadLen
|
||||
}
|
||||
e.payload = append(e.payload, (id<<4 | uint8(len(buf)-1)))
|
||||
e.payload = append(e.payload, buf...)
|
||||
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIDs returns the available IDs.
|
||||
func (e *OneByteHeaderExtension) GetIDs() []uint8 {
|
||||
ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4]))
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n] >> 4
|
||||
payloadLen := int(e.payload[n]&^0xF0 + 1)
|
||||
n++
|
||||
|
||||
if extid == headerExtensionIDReserved {
|
||||
break
|
||||
}
|
||||
|
||||
ids = append(ids, extid)
|
||||
n += payloadLen
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// Get returns the payload of the extension with the given ID.
|
||||
func (e *OneByteHeaderExtension) Get(id uint8) []byte {
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n] >> 4
|
||||
payloadLen := int(e.payload[n]&^0xF0 + 1)
|
||||
n++
|
||||
|
||||
if extid == id {
|
||||
return e.payload[n : n+payloadLen]
|
||||
}
|
||||
n += payloadLen
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del deletes the extension with the specified ID.
|
||||
func (e *OneByteHeaderExtension) Del(id uint8) error {
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n] >> 4
|
||||
payloadLen := int(e.payload[n]&^0xF0 + 1)
|
||||
|
||||
if extid == id {
|
||||
e.payload = append(e.payload[:n], e.payload[n+1+payloadLen:]...)
|
||||
return nil
|
||||
}
|
||||
n += payloadLen + 1
|
||||
}
|
||||
return errHeaderExtensionNotFound
|
||||
}
|
||||
|
||||
// Unmarshal parses the extension payload.
|
||||
func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
|
||||
profile := binary.BigEndian.Uint16(buf[0:2])
|
||||
if profile != headerExtensionProfileOneByte {
|
||||
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
|
||||
}
|
||||
e.payload = buf
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// Marshal returns the extension payload.
|
||||
func (e OneByteHeaderExtension) Marshal() ([]byte, error) {
|
||||
return e.payload, nil
|
||||
}
|
||||
|
||||
// MarshalTo writes the extension payload to the given buffer.
|
||||
func (e OneByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
|
||||
size := e.MarshalSize()
|
||||
if size > len(buf) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
return copy(buf, e.payload), nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of the extension payload.
|
||||
func (e OneByteHeaderExtension) MarshalSize() int {
|
||||
return len(e.payload)
|
||||
}
|
||||
|
||||
// TwoByteHeaderExtension is an RFC8285 two-byte header extension.
|
||||
type TwoByteHeaderExtension struct {
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// Set sets the extension payload for the specified ID.
|
||||
func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error {
|
||||
if id < 1 || id > 255 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
|
||||
}
|
||||
if len(buf) > 255 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len(buf))
|
||||
}
|
||||
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n]
|
||||
n++
|
||||
|
||||
payloadLen := int(e.payload[n])
|
||||
n++
|
||||
|
||||
if extid == id {
|
||||
e.payload = append(e.payload[:n+2], append(buf, e.payload[n+2+payloadLen:]...)...)
|
||||
return nil
|
||||
}
|
||||
n += payloadLen
|
||||
}
|
||||
e.payload = append(e.payload, id, uint8(len(buf)))
|
||||
e.payload = append(e.payload, buf...)
|
||||
binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIDs returns the available IDs.
|
||||
func (e *TwoByteHeaderExtension) GetIDs() []uint8 {
|
||||
ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4]))
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n]
|
||||
n++
|
||||
|
||||
payloadLen := int(e.payload[n])
|
||||
n++
|
||||
|
||||
ids = append(ids, extid)
|
||||
n += payloadLen
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// Get returns the payload of the extension with the given ID.
|
||||
func (e *TwoByteHeaderExtension) Get(id uint8) []byte {
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n]
|
||||
n++
|
||||
|
||||
payloadLen := int(e.payload[n])
|
||||
n++
|
||||
|
||||
if extid == id {
|
||||
return e.payload[n : n+payloadLen]
|
||||
}
|
||||
n += payloadLen
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del deletes the extension with the specified ID.
|
||||
func (e *TwoByteHeaderExtension) Del(id uint8) error {
|
||||
for n := 4; n < len(e.payload); {
|
||||
if e.payload[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
extid := e.payload[n]
|
||||
|
||||
payloadLen := int(e.payload[n+1])
|
||||
|
||||
if extid == id {
|
||||
e.payload = append(e.payload[:n], e.payload[n+2+payloadLen:]...)
|
||||
return nil
|
||||
}
|
||||
n += payloadLen + 2
|
||||
}
|
||||
return errHeaderExtensionNotFound
|
||||
}
|
||||
|
||||
// Unmarshal parses the extension payload.
|
||||
func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) {
|
||||
profile := binary.BigEndian.Uint16(buf[0:2])
|
||||
if profile != headerExtensionProfileTwoByte {
|
||||
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
|
||||
}
|
||||
e.payload = buf
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// Marshal returns the extension payload.
|
||||
func (e TwoByteHeaderExtension) Marshal() ([]byte, error) {
|
||||
return e.payload, nil
|
||||
}
|
||||
|
||||
// MarshalTo marshals the extension to the given buffer.
|
||||
func (e TwoByteHeaderExtension) MarshalTo(buf []byte) (int, error) {
|
||||
size := e.MarshalSize()
|
||||
if size > len(buf) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
return copy(buf, e.payload), nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of the extension payload.
|
||||
func (e TwoByteHeaderExtension) MarshalSize() int {
|
||||
return len(e.payload)
|
||||
}
|
||||
|
||||
// RawExtension represents an RFC3550 header extension.
|
||||
type RawExtension struct {
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// Set sets the extension payload for the specified ID.
|
||||
func (e *RawExtension) Set(id uint8, payload []byte) error {
|
||||
if id != 0 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
|
||||
}
|
||||
e.payload = payload
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetIDs returns the available IDs.
|
||||
func (e *RawExtension) GetIDs() []uint8 {
|
||||
return []uint8{0}
|
||||
}
|
||||
|
||||
// Get returns the payload of the extension with the given ID.
|
||||
func (e *RawExtension) Get(id uint8) []byte {
|
||||
if id == 0 {
|
||||
return e.payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del deletes the extension with the specified ID.
|
||||
func (e *RawExtension) Del(id uint8) error {
|
||||
if id == 0 {
|
||||
e.payload = nil
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
|
||||
}
|
||||
|
||||
// Unmarshal parses the extension from the given buffer.
|
||||
func (e *RawExtension) Unmarshal(buf []byte) (int, error) {
|
||||
profile := binary.BigEndian.Uint16(buf[0:2])
|
||||
if profile == headerExtensionProfileOneByte || profile == headerExtensionProfileTwoByte {
|
||||
return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2])
|
||||
}
|
||||
e.payload = buf
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// Marshal returns the raw extension payload.
|
||||
func (e RawExtension) Marshal() ([]byte, error) {
|
||||
return e.payload, nil
|
||||
}
|
||||
|
||||
// MarshalTo marshals the extension to the given buffer.
|
||||
func (e RawExtension) MarshalTo(buf []byte) (int, error) {
|
||||
size := e.MarshalSize()
|
||||
if size > len(buf) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
return copy(buf, e.payload), nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of the extension when marshaled.
|
||||
func (e RawExtension) MarshalSize() int {
|
||||
return len(e.payload)
|
||||
}
|
||||
540
server/vendor/github.com/pion/rtp/packet.go
generated
vendored
Normal file
540
server/vendor/github.com/pion/rtp/packet.go
generated
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Extension RTP Header extension
|
||||
type Extension struct {
|
||||
id uint8
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// Header represents an RTP packet header
|
||||
type Header struct {
|
||||
Version uint8
|
||||
Padding bool
|
||||
Extension bool
|
||||
Marker bool
|
||||
PayloadType uint8
|
||||
SequenceNumber uint16
|
||||
Timestamp uint32
|
||||
SSRC uint32
|
||||
CSRC []uint32
|
||||
ExtensionProfile uint16
|
||||
Extensions []Extension
|
||||
|
||||
// Deprecated: will be removed in a future version.
|
||||
PayloadOffset int
|
||||
}
|
||||
|
||||
// Packet represents an RTP Packet
|
||||
type Packet struct {
|
||||
Header
|
||||
Payload []byte
|
||||
PaddingSize byte
|
||||
|
||||
// Deprecated: will be removed in a future version.
|
||||
Raw []byte
|
||||
}
|
||||
|
||||
const (
|
||||
headerLength = 4
|
||||
versionShift = 6
|
||||
versionMask = 0x3
|
||||
paddingShift = 5
|
||||
paddingMask = 0x1
|
||||
extensionShift = 4
|
||||
extensionMask = 0x1
|
||||
extensionProfileOneByte = 0xBEDE
|
||||
extensionProfileTwoByte = 0x1000
|
||||
extensionIDReserved = 0xF
|
||||
ccMask = 0xF
|
||||
markerShift = 7
|
||||
markerMask = 0x1
|
||||
ptMask = 0x7F
|
||||
seqNumOffset = 2
|
||||
seqNumLength = 2
|
||||
timestampOffset = 4
|
||||
timestampLength = 4
|
||||
ssrcOffset = 8
|
||||
ssrcLength = 4
|
||||
csrcOffset = 12
|
||||
csrcLength = 4
|
||||
)
|
||||
|
||||
// String helps with debugging by printing packet information in a readable way
|
||||
func (p Packet) String() string {
|
||||
out := "RTP PACKET:\n"
|
||||
|
||||
out += fmt.Sprintf("\tVersion: %v\n", p.Version)
|
||||
out += fmt.Sprintf("\tMarker: %v\n", p.Marker)
|
||||
out += fmt.Sprintf("\tPayload Type: %d\n", p.PayloadType)
|
||||
out += fmt.Sprintf("\tSequence Number: %d\n", p.SequenceNumber)
|
||||
out += fmt.Sprintf("\tTimestamp: %d\n", p.Timestamp)
|
||||
out += fmt.Sprintf("\tSSRC: %d (%x)\n", p.SSRC, p.SSRC)
|
||||
out += fmt.Sprintf("\tPayload Length: %d\n", len(p.Payload))
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the Header.
|
||||
// It returns the number of bytes read n and any error.
|
||||
func (h *Header) Unmarshal(buf []byte) (n int, err error) { //nolint:gocognit
|
||||
if len(buf) < headerLength {
|
||||
return 0, fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(buf), headerLength)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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|X| CC |M| PT | sequence number |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | timestamp |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | synchronization source (SSRC) identifier |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* | contributing source (CSRC) identifiers |
|
||||
* | .... |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
h.Version = buf[0] >> versionShift & versionMask
|
||||
h.Padding = (buf[0] >> paddingShift & paddingMask) > 0
|
||||
h.Extension = (buf[0] >> extensionShift & extensionMask) > 0
|
||||
nCSRC := int(buf[0] & ccMask)
|
||||
if cap(h.CSRC) < nCSRC || h.CSRC == nil {
|
||||
h.CSRC = make([]uint32, nCSRC)
|
||||
} else {
|
||||
h.CSRC = h.CSRC[:nCSRC]
|
||||
}
|
||||
|
||||
n = csrcOffset + (nCSRC * csrcLength)
|
||||
if len(buf) < n {
|
||||
return n, fmt.Errorf("size %d < %d: %w", len(buf), n,
|
||||
errHeaderSizeInsufficient)
|
||||
}
|
||||
|
||||
h.Marker = (buf[1] >> markerShift & markerMask) > 0
|
||||
h.PayloadType = buf[1] & ptMask
|
||||
|
||||
h.SequenceNumber = binary.BigEndian.Uint16(buf[seqNumOffset : seqNumOffset+seqNumLength])
|
||||
h.Timestamp = binary.BigEndian.Uint32(buf[timestampOffset : timestampOffset+timestampLength])
|
||||
h.SSRC = binary.BigEndian.Uint32(buf[ssrcOffset : ssrcOffset+ssrcLength])
|
||||
|
||||
for i := range h.CSRC {
|
||||
offset := csrcOffset + (i * csrcLength)
|
||||
h.CSRC[i] = binary.BigEndian.Uint32(buf[offset:])
|
||||
}
|
||||
|
||||
if h.Extensions != nil {
|
||||
h.Extensions = h.Extensions[:0]
|
||||
}
|
||||
|
||||
if h.Extension {
|
||||
if expected := n + 4; len(buf) < expected {
|
||||
return n, fmt.Errorf("size %d < %d: %w",
|
||||
len(buf), expected,
|
||||
errHeaderSizeInsufficientForExtension,
|
||||
)
|
||||
}
|
||||
|
||||
h.ExtensionProfile = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
extensionLength := int(binary.BigEndian.Uint16(buf[n:])) * 4
|
||||
n += 2
|
||||
extensionEnd := n + extensionLength
|
||||
|
||||
if len(buf) < extensionEnd {
|
||||
return n, fmt.Errorf("size %d < %d: %w", len(buf), extensionEnd, errHeaderSizeInsufficientForExtension)
|
||||
}
|
||||
|
||||
if h.ExtensionProfile == extensionProfileOneByte || h.ExtensionProfile == extensionProfileTwoByte {
|
||||
var (
|
||||
extid uint8
|
||||
payloadLen int
|
||||
)
|
||||
|
||||
for n < extensionEnd {
|
||||
if buf[n] == 0x00 { // padding
|
||||
n++
|
||||
continue
|
||||
}
|
||||
|
||||
if h.ExtensionProfile == extensionProfileOneByte {
|
||||
extid = buf[n] >> 4
|
||||
payloadLen = int(buf[n]&^0xF0 + 1)
|
||||
n++
|
||||
|
||||
if extid == extensionIDReserved {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
extid = buf[n]
|
||||
n++
|
||||
|
||||
if len(buf) <= n {
|
||||
return n, fmt.Errorf("size %d < %d: %w", len(buf), n, errHeaderSizeInsufficientForExtension)
|
||||
}
|
||||
|
||||
payloadLen = int(buf[n])
|
||||
n++
|
||||
}
|
||||
|
||||
if extensionPayloadEnd := n + payloadLen; len(buf) <= extensionPayloadEnd {
|
||||
return n, fmt.Errorf("size %d < %d: %w", len(buf), extensionPayloadEnd, errHeaderSizeInsufficientForExtension)
|
||||
}
|
||||
|
||||
extension := Extension{id: extid, payload: buf[n : n+payloadLen]}
|
||||
h.Extensions = append(h.Extensions, extension)
|
||||
n += payloadLen
|
||||
}
|
||||
} else {
|
||||
// RFC3550 Extension
|
||||
extension := Extension{id: 0, payload: buf[n:extensionEnd]}
|
||||
h.Extensions = append(h.Extensions, extension)
|
||||
n += len(h.Extensions[0].payload)
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the Packet.
|
||||
func (p *Packet) Unmarshal(buf []byte) error {
|
||||
n, err := p.Header.Unmarshal(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
end := len(buf)
|
||||
if p.Header.Padding {
|
||||
if end <= n {
|
||||
return errTooSmall
|
||||
}
|
||||
p.PaddingSize = buf[end-1]
|
||||
end -= int(p.PaddingSize)
|
||||
}
|
||||
if end < n {
|
||||
return errTooSmall
|
||||
}
|
||||
|
||||
p.Payload = buf[n:end]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal serializes the header into bytes.
|
||||
func (h Header) Marshal() (buf []byte, err error) {
|
||||
buf = make([]byte, h.MarshalSize())
|
||||
|
||||
n, err := h.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
// MarshalTo serializes the header and writes to the buffer.
|
||||
func (h Header) MarshalTo(buf []byte) (n int, err error) {
|
||||
/*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |V=2|P|X| CC |M| PT | sequence number |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | timestamp |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | synchronization source (SSRC) identifier |
|
||||
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
* | contributing source (CSRC) identifiers |
|
||||
* | .... |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
size := h.MarshalSize()
|
||||
if size > len(buf) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
// The first byte contains the version, padding bit, extension bit,
|
||||
// and csrc size.
|
||||
buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC))
|
||||
if h.Padding {
|
||||
buf[0] |= 1 << paddingShift
|
||||
}
|
||||
|
||||
if h.Extension {
|
||||
buf[0] |= 1 << extensionShift
|
||||
}
|
||||
|
||||
// The second byte contains the marker bit and payload type.
|
||||
buf[1] = h.PayloadType
|
||||
if h.Marker {
|
||||
buf[1] |= 1 << markerShift
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[2:4], h.SequenceNumber)
|
||||
binary.BigEndian.PutUint32(buf[4:8], h.Timestamp)
|
||||
binary.BigEndian.PutUint32(buf[8:12], h.SSRC)
|
||||
|
||||
n = 12
|
||||
for _, csrc := range h.CSRC {
|
||||
binary.BigEndian.PutUint32(buf[n:n+4], csrc)
|
||||
n += 4
|
||||
}
|
||||
|
||||
if h.Extension {
|
||||
extHeaderPos := n
|
||||
binary.BigEndian.PutUint16(buf[n+0:n+2], h.ExtensionProfile)
|
||||
n += 4
|
||||
startExtensionsPos := n
|
||||
|
||||
switch h.ExtensionProfile {
|
||||
// RFC 8285 RTP One Byte Header Extension
|
||||
case extensionProfileOneByte:
|
||||
for _, extension := range h.Extensions {
|
||||
buf[n] = extension.id<<4 | (uint8(len(extension.payload)) - 1)
|
||||
n++
|
||||
n += copy(buf[n:], extension.payload)
|
||||
}
|
||||
// RFC 8285 RTP Two Byte Header Extension
|
||||
case extensionProfileTwoByte:
|
||||
for _, extension := range h.Extensions {
|
||||
buf[n] = extension.id
|
||||
n++
|
||||
buf[n] = uint8(len(extension.payload))
|
||||
n++
|
||||
n += copy(buf[n:], extension.payload)
|
||||
}
|
||||
default: // RFC3550 Extension
|
||||
extlen := len(h.Extensions[0].payload)
|
||||
if extlen%4 != 0 {
|
||||
// the payload must be in 32-bit words.
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
n += copy(buf[n:], h.Extensions[0].payload)
|
||||
}
|
||||
|
||||
// calculate extensions size and round to 4 bytes boundaries
|
||||
extSize := n - startExtensionsPos
|
||||
roundedExtSize := ((extSize + 3) / 4) * 4
|
||||
|
||||
binary.BigEndian.PutUint16(buf[extHeaderPos+2:extHeaderPos+4], uint16(roundedExtSize/4))
|
||||
|
||||
// add padding to reach 4 bytes boundaries
|
||||
for i := 0; i < roundedExtSize-extSize; i++ {
|
||||
buf[n] = 0
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of the header once marshaled.
|
||||
func (h Header) MarshalSize() int {
|
||||
// NOTE: Be careful to match the MarshalTo() method.
|
||||
size := 12 + (len(h.CSRC) * csrcLength)
|
||||
|
||||
if h.Extension {
|
||||
extSize := 4
|
||||
|
||||
switch h.ExtensionProfile {
|
||||
// RFC 8285 RTP One Byte Header Extension
|
||||
case extensionProfileOneByte:
|
||||
for _, extension := range h.Extensions {
|
||||
extSize += 1 + len(extension.payload)
|
||||
}
|
||||
// RFC 8285 RTP Two Byte Header Extension
|
||||
case extensionProfileTwoByte:
|
||||
for _, extension := range h.Extensions {
|
||||
extSize += 2 + len(extension.payload)
|
||||
}
|
||||
default:
|
||||
extSize += len(h.Extensions[0].payload)
|
||||
}
|
||||
|
||||
// extensions size must have 4 bytes boundaries
|
||||
size += ((extSize + 3) / 4) * 4
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
// SetExtension sets an RTP header extension
|
||||
func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocognit
|
||||
if h.Extension {
|
||||
switch h.ExtensionProfile {
|
||||
// RFC 8285 RTP One Byte Header Extension
|
||||
case extensionProfileOneByte:
|
||||
if id < 1 || id > 14 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id)
|
||||
}
|
||||
if len(payload) > 16 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(payload))
|
||||
}
|
||||
// RFC 8285 RTP Two Byte Header Extension
|
||||
case extensionProfileTwoByte:
|
||||
if id < 1 || id > 255 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
|
||||
}
|
||||
if len(payload) > 255 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len(payload))
|
||||
}
|
||||
default: // RFC3550 Extension
|
||||
if id != 0 {
|
||||
return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Update existing if it exists else add new extension
|
||||
for i, extension := range h.Extensions {
|
||||
if extension.id == id {
|
||||
h.Extensions[i].payload = payload
|
||||
return nil
|
||||
}
|
||||
}
|
||||
h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
|
||||
return nil
|
||||
}
|
||||
|
||||
// No existing header extensions
|
||||
h.Extension = true
|
||||
|
||||
switch payloadLen := len(payload); {
|
||||
case payloadLen <= 16:
|
||||
h.ExtensionProfile = extensionProfileOneByte
|
||||
case payloadLen > 16 && payloadLen < 256:
|
||||
h.ExtensionProfile = extensionProfileTwoByte
|
||||
}
|
||||
|
||||
h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExtensionIDs returns an extension id array
|
||||
func (h *Header) GetExtensionIDs() []uint8 {
|
||||
if !h.Extension {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(h.Extensions) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ids := make([]uint8, 0, len(h.Extensions))
|
||||
for _, extension := range h.Extensions {
|
||||
ids = append(ids, extension.id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// GetExtension returns an RTP header extension
|
||||
func (h *Header) GetExtension(id uint8) []byte {
|
||||
if !h.Extension {
|
||||
return nil
|
||||
}
|
||||
for _, extension := range h.Extensions {
|
||||
if extension.id == id {
|
||||
return extension.payload
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelExtension Removes an RTP Header extension
|
||||
func (h *Header) DelExtension(id uint8) error {
|
||||
if !h.Extension {
|
||||
return errHeaderExtensionsNotEnabled
|
||||
}
|
||||
for i, extension := range h.Extensions {
|
||||
if extension.id == id {
|
||||
h.Extensions = append(h.Extensions[:i], h.Extensions[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errHeaderExtensionNotFound
|
||||
}
|
||||
|
||||
// Marshal serializes the packet into bytes.
|
||||
func (p Packet) Marshal() (buf []byte, err error) {
|
||||
buf = make([]byte, p.MarshalSize())
|
||||
|
||||
n, err := p.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
// MarshalTo serializes the packet and writes to the buffer.
|
||||
func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
|
||||
if p.Header.Padding && p.PaddingSize == 0 {
|
||||
return 0, errInvalidRTPPadding
|
||||
}
|
||||
|
||||
n, err = p.Header.MarshalTo(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Make sure the buffer is large enough to hold the packet.
|
||||
if n+len(p.Payload)+int(p.PaddingSize) > len(buf) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
m := copy(buf[n:], p.Payload)
|
||||
|
||||
if p.Header.Padding {
|
||||
buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize
|
||||
}
|
||||
|
||||
return n + m + int(p.PaddingSize), nil
|
||||
}
|
||||
|
||||
// MarshalSize returns the size of the packet once marshaled.
|
||||
func (p Packet) MarshalSize() int {
|
||||
return p.Header.MarshalSize() + len(p.Payload) + int(p.PaddingSize)
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of p.
|
||||
func (p Packet) Clone() *Packet {
|
||||
clone := &Packet{}
|
||||
clone.Header = p.Header.Clone()
|
||||
if p.Payload != nil {
|
||||
clone.Payload = make([]byte, len(p.Payload))
|
||||
copy(clone.Payload, p.Payload)
|
||||
}
|
||||
clone.PaddingSize = p.PaddingSize
|
||||
return clone
|
||||
}
|
||||
|
||||
// Clone returns a deep copy h.
|
||||
func (h Header) Clone() Header {
|
||||
clone := h
|
||||
if h.CSRC != nil {
|
||||
clone.CSRC = make([]uint32, len(h.CSRC))
|
||||
copy(clone.CSRC, h.CSRC)
|
||||
}
|
||||
if h.Extensions != nil {
|
||||
ext := make([]Extension, len(h.Extensions))
|
||||
for i, e := range h.Extensions {
|
||||
ext[i] = e
|
||||
if e.payload != nil {
|
||||
ext[i].payload = make([]byte, len(e.payload))
|
||||
copy(ext[i].payload, e.payload)
|
||||
}
|
||||
}
|
||||
clone.Extensions = ext
|
||||
}
|
||||
return clone
|
||||
}
|
||||
138
server/vendor/github.com/pion/rtp/packetizer.go
generated
vendored
Normal file
138
server/vendor/github.com/pion/rtp/packetizer.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Payloader payloads a byte array for use as rtp.Packet payloads
|
||||
type Payloader interface {
|
||||
Payload(mtu uint16, payload []byte) [][]byte
|
||||
}
|
||||
|
||||
// Packetizer packetizes a payload
|
||||
type Packetizer interface {
|
||||
Packetize(payload []byte, samples uint32) []*Packet
|
||||
GeneratePadding(samples uint32) []*Packet
|
||||
EnableAbsSendTime(value int)
|
||||
SkipSamples(skippedSamples uint32)
|
||||
}
|
||||
|
||||
type packetizer struct {
|
||||
MTU uint16
|
||||
PayloadType uint8
|
||||
SSRC uint32
|
||||
Payloader Payloader
|
||||
Sequencer Sequencer
|
||||
Timestamp uint32
|
||||
|
||||
// Deprecated: will be removed in a future version.
|
||||
ClockRate uint32
|
||||
|
||||
extensionNumbers struct { // put extension numbers in here. If they're 0, the extension is disabled (0 is not a legal extension number)
|
||||
AbsSendTime int // http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
}
|
||||
timegen func() time.Time
|
||||
}
|
||||
|
||||
// NewPacketizer returns a new instance of a Packetizer for a specific payloader
|
||||
func NewPacketizer(mtu uint16, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
|
||||
return &packetizer{
|
||||
MTU: mtu,
|
||||
PayloadType: pt,
|
||||
SSRC: ssrc,
|
||||
Payloader: payloader,
|
||||
Sequencer: sequencer,
|
||||
Timestamp: globalMathRandomGenerator.Uint32(),
|
||||
ClockRate: clockRate,
|
||||
timegen: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *packetizer) EnableAbsSendTime(value int) {
|
||||
p.extensionNumbers.AbsSendTime = value
|
||||
}
|
||||
|
||||
// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets
|
||||
func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
|
||||
// Guard against an empty payload
|
||||
if len(payload) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
payloads := p.Payloader.Payload(p.MTU-12, payload)
|
||||
packets := make([]*Packet, len(payloads))
|
||||
|
||||
for i, pp := range payloads {
|
||||
packets[i] = &Packet{
|
||||
Header: Header{
|
||||
Version: 2,
|
||||
Padding: false,
|
||||
Extension: false,
|
||||
Marker: i == len(payloads)-1,
|
||||
PayloadType: p.PayloadType,
|
||||
SequenceNumber: p.Sequencer.NextSequenceNumber(),
|
||||
Timestamp: p.Timestamp, // Figure out how to do timestamps
|
||||
SSRC: p.SSRC,
|
||||
CSRC: []uint32{},
|
||||
},
|
||||
Payload: pp,
|
||||
}
|
||||
}
|
||||
p.Timestamp += samples
|
||||
|
||||
if len(packets) != 0 && p.extensionNumbers.AbsSendTime != 0 {
|
||||
sendTime := NewAbsSendTimeExtension(p.timegen())
|
||||
// apply http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
|
||||
b, err := sendTime.Marshal()
|
||||
if err != nil {
|
||||
return nil // never happens
|
||||
}
|
||||
err = packets[len(packets)-1].SetExtension(uint8(p.extensionNumbers.AbsSendTime), b)
|
||||
if err != nil {
|
||||
return nil // never happens
|
||||
}
|
||||
}
|
||||
|
||||
return packets
|
||||
}
|
||||
|
||||
// GeneratePadding returns required padding-only packages
|
||||
func (p *packetizer) GeneratePadding(samples uint32) []*Packet {
|
||||
// Guard against an empty payload
|
||||
if samples == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
packets := make([]*Packet, samples)
|
||||
|
||||
for i := 0; i < int(samples); i++ {
|
||||
pp := make([]byte, 255)
|
||||
pp[254] = 255
|
||||
|
||||
packets[i] = &Packet{
|
||||
Header: Header{
|
||||
Version: 2,
|
||||
Padding: true,
|
||||
Extension: false,
|
||||
Marker: false,
|
||||
PayloadType: p.PayloadType,
|
||||
SequenceNumber: p.Sequencer.NextSequenceNumber(),
|
||||
Timestamp: p.Timestamp, // Use latest timestamp
|
||||
SSRC: p.SSRC,
|
||||
CSRC: []uint32{},
|
||||
},
|
||||
Payload: pp,
|
||||
}
|
||||
}
|
||||
|
||||
return packets
|
||||
}
|
||||
|
||||
// SkipSamples causes a gap in sample count between Packetize requests so the
|
||||
// RTP payloads produced have a gap in timestamps
|
||||
func (p *packetizer) SkipSamples(skippedSamples uint32) {
|
||||
p.Timestamp += skippedSamples
|
||||
}
|
||||
9
server/vendor/github.com/pion/rtp/partitionheadchecker.go
generated
vendored
Normal file
9
server/vendor/github.com/pion/rtp/partitionheadchecker.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
// PartitionHeadChecker is the interface that checks whether the packet is keyframe or not
|
||||
type PartitionHeadChecker interface {
|
||||
IsPartitionHead([]byte) bool
|
||||
}
|
||||
68
server/vendor/github.com/pion/rtp/payload_types.go
generated
vendored
Normal file
68
server/vendor/github.com/pion/rtp/payload_types.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: 2024 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
// https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
|
||||
// https://en.wikipedia.org/wiki/RTP_payload_formats
|
||||
|
||||
// Audio Payload Types as defined in https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
|
||||
const (
|
||||
// PayloadTypePCMU is a payload type for ITU-T G.711 PCM μ-Law audio 64 kbit/s (RFC 3551).
|
||||
PayloadTypePCMU = 0
|
||||
// PayloadTypeGSM is a payload type for European GSM Full Rate audio 13 kbit/s (GSM 06.10).
|
||||
PayloadTypeGSM = 3
|
||||
// PayloadTypeG723 is a payload type for ITU-T G.723.1 audio (RFC 3551).
|
||||
PayloadTypeG723 = 4
|
||||
// PayloadTypeDVI4_8000 is a payload type for IMA ADPCM audio 32 kbit/s (RFC 3551).
|
||||
PayloadTypeDVI4_8000 = 5
|
||||
// PayloadTypeDVI4_16000 is a payload type for IMA ADPCM audio 64 kbit/s (RFC 3551).
|
||||
PayloadTypeDVI4_16000 = 6
|
||||
// PayloadTypeLPC is a payload type for Experimental Linear Predictive Coding audio 5.6 kbit/s (RFC 3551).
|
||||
PayloadTypeLPC = 7
|
||||
// PayloadTypePCMA is a payload type for ITU-T G.711 PCM A-Law audio 64 kbit/s (RFC 3551).
|
||||
PayloadTypePCMA = 8
|
||||
// PayloadTypeG722 is a payload type for ITU-T G.722 audio 64 kbit/s (RFC 3551).
|
||||
PayloadTypeG722 = 9
|
||||
// PayloadTypeL16Stereo is a payload type for Linear PCM 16-bit Stereo audio 1411.2 kbit/s, uncompressed (RFC 3551).
|
||||
PayloadTypeL16Stereo = 10
|
||||
// PayloadTypeL16Mono is a payload type for Linear PCM 16-bit audio 705.6 kbit/s, uncompressed (RFC 3551).
|
||||
PayloadTypeL16Mono = 11
|
||||
// PayloadTypeQCELP is a payload type for Qualcomm Code Excited Linear Prediction (RFC 2658, RFC 3551).
|
||||
PayloadTypeQCELP = 12
|
||||
// PayloadTypeCN is a payload type for Comfort noise (RFC 3389).
|
||||
PayloadTypeCN = 13
|
||||
// PayloadTypeMPA is a payload type for MPEG-1 or MPEG-2 audio only (RFC 3551, RFC 2250).
|
||||
PayloadTypeMPA = 14
|
||||
// PayloadTypeG728 is a payload type for ITU-T G.728 audio 16 kbit/s (RFC 3551).
|
||||
PayloadTypeG728 = 15
|
||||
// PayloadTypeDVI4_11025 is a payload type for IMA ADPCM audio 44.1 kbit/s (RFC 3551).
|
||||
PayloadTypeDVI4_11025 = 16
|
||||
// PayloadTypeDVI4_22050 is a payload type for IMA ADPCM audio 88.2 kbit/s (RFC 3551).
|
||||
PayloadTypeDVI4_22050 = 17
|
||||
// PayloadTypeG729 is a payload type for ITU-T G.729 and G.729a audio 8 kbit/s (RFC 3551, RFC 3555).
|
||||
PayloadTypeG729 = 18
|
||||
)
|
||||
|
||||
// Video Payload Types as defined in https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml
|
||||
const (
|
||||
// PayloadTypeCELLB is a payload type for Sun CellB video (RFC 2029).
|
||||
PayloadTypeCELLB = 25
|
||||
// PayloadTypeJPEG is a payload type for JPEG video (RFC 2435).
|
||||
PayloadTypeJPEG = 26
|
||||
// PayloadTypeNV is a payload type for Xerox PARC's Network Video (nv, RFC 3551).
|
||||
PayloadTypeNV = 28
|
||||
// PayloadTypeH261 is a payload type for ITU-T H.261 video (RFC 4587).
|
||||
PayloadTypeH261 = 31
|
||||
// PayloadTypeMPV is a payload type for MPEG-1 and MPEG-2 video (RFC 2250).
|
||||
PayloadTypeMPV = 32
|
||||
// PayloadTypeMP2T is a payload type for MPEG-2 transport stream (RFC 2250).
|
||||
PayloadTypeMP2T = 33
|
||||
// PayloadTypeH263 is a payload type for H.263 video, first version (1996, RFC 3551, RFC 2190).
|
||||
PayloadTypeH263 = 34
|
||||
)
|
||||
|
||||
const (
|
||||
// PayloadTypeFirstDynamic is a first non-static payload type.
|
||||
PayloadTypeFirstDynamic = 35
|
||||
)
|
||||
50
server/vendor/github.com/pion/rtp/playoutdelayextension.go
generated
vendored
Normal file
50
server/vendor/github.com/pion/rtp/playoutdelayextension.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
playoutDelayExtensionSize = 3
|
||||
playoutDelayMaxValue = (1 << 12) - 1
|
||||
)
|
||||
|
||||
var errPlayoutDelayInvalidValue = errors.New("invalid playout delay value")
|
||||
|
||||
// PlayoutDelayExtension is a extension payload format in
|
||||
// http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | len=2 | MIN delay | MAX delay |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type PlayoutDelayExtension struct {
|
||||
minDelay, maxDelay uint16
|
||||
}
|
||||
|
||||
// Marshal serializes the members to buffer
|
||||
func (p PlayoutDelayExtension) Marshal() ([]byte, error) {
|
||||
if p.minDelay > playoutDelayMaxValue || p.maxDelay > playoutDelayMaxValue {
|
||||
return nil, errPlayoutDelayInvalidValue
|
||||
}
|
||||
|
||||
return []byte{
|
||||
byte(p.minDelay >> 4),
|
||||
byte(p.minDelay<<4) | byte(p.maxDelay>>8),
|
||||
byte(p.maxDelay),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the members
|
||||
func (p *PlayoutDelayExtension) Unmarshal(rawData []byte) error {
|
||||
if len(rawData) < playoutDelayExtensionSize {
|
||||
return errTooSmall
|
||||
}
|
||||
p.minDelay = binary.BigEndian.Uint16(rawData[0:2]) >> 4
|
||||
p.maxDelay = binary.BigEndian.Uint16(rawData[1:3]) & 0x0FFF
|
||||
return nil
|
||||
}
|
||||
11
server/vendor/github.com/pion/rtp/rand.go
generated
vendored
Normal file
11
server/vendor/github.com/pion/rtp/rand.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"github.com/pion/randutil"
|
||||
)
|
||||
|
||||
// Use global random generator to properly seed by crypto grade random.
|
||||
var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
|
||||
6
server/vendor/github.com/pion/rtp/renovate.json
generated
vendored
Normal file
6
server/vendor/github.com/pion/rtp/renovate.json
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>pion/renovate-config"
|
||||
]
|
||||
}
|
||||
5
server/vendor/github.com/pion/rtp/rtp.go
generated
vendored
Normal file
5
server/vendor/github.com/pion/rtp/rtp.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package rtp provides RTP packetizer and depacketizer
|
||||
package rtp
|
||||
60
server/vendor/github.com/pion/rtp/sequencer.go
generated
vendored
Normal file
60
server/vendor/github.com/pion/rtp/sequencer.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Sequencer generates sequential sequence numbers for building RTP packets
|
||||
type Sequencer interface {
|
||||
NextSequenceNumber() uint16
|
||||
RollOverCount() uint64
|
||||
}
|
||||
|
||||
// NewRandomSequencer returns a new sequencer starting from a random sequence
|
||||
// number
|
||||
func NewRandomSequencer() Sequencer {
|
||||
return &sequencer{
|
||||
sequenceNumber: uint16(globalMathRandomGenerator.Intn(math.MaxUint16)),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFixedSequencer returns a new sequencer starting from a specific
|
||||
// sequence number
|
||||
func NewFixedSequencer(s uint16) Sequencer {
|
||||
return &sequencer{
|
||||
sequenceNumber: s - 1, // -1 because the first sequence number prepends 1
|
||||
}
|
||||
}
|
||||
|
||||
type sequencer struct {
|
||||
sequenceNumber uint16
|
||||
rollOverCount uint64
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// NextSequenceNumber increment and returns a new sequence number for
|
||||
// building RTP packets
|
||||
func (s *sequencer) NextSequenceNumber() uint16 {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.sequenceNumber++
|
||||
if s.sequenceNumber == 0 {
|
||||
s.rollOverCount++
|
||||
}
|
||||
|
||||
return s.sequenceNumber
|
||||
}
|
||||
|
||||
// RollOverCount returns the amount of times the 16bit sequence number
|
||||
// has wrapped
|
||||
func (s *sequencer) RollOverCount() uint64 {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
return s.rollOverCount
|
||||
}
|
||||
42
server/vendor/github.com/pion/rtp/transportccextension.go
generated
vendored
Normal file
42
server/vendor/github.com/pion/rtp/transportccextension.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
// transport-wide sequence
|
||||
transportCCExtensionSize = 2
|
||||
)
|
||||
|
||||
// TransportCCExtension is a extension payload format in
|
||||
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | 0xBE | 0xDE | length=1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | L=1 |transport-wide sequence number | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type TransportCCExtension struct {
|
||||
TransportSequence uint16
|
||||
}
|
||||
|
||||
// Marshal serializes the members to buffer
|
||||
func (t TransportCCExtension) Marshal() ([]byte, error) {
|
||||
buf := make([]byte, transportCCExtensionSize)
|
||||
binary.BigEndian.PutUint16(buf[0:2], t.TransportSequence)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the members
|
||||
func (t *TransportCCExtension) Unmarshal(rawData []byte) error {
|
||||
if len(rawData) < transportCCExtensionSize {
|
||||
return errTooSmall
|
||||
}
|
||||
t.TransportSequence = binary.BigEndian.Uint16(rawData[0:2])
|
||||
return nil
|
||||
}
|
||||
360
server/vendor/github.com/pion/rtp/vlaextension.go
generated
vendored
Normal file
360
server/vendor/github.com/pion/rtp/vlaextension.go
generated
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/rtp/codecs/av1/obu"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVLATooShort = errors.New("VLA payload too short") // ErrVLATooShort is returned when payload is too short
|
||||
ErrVLAInvalidStreamCount = errors.New("invalid RTP stream count in VLA") // ErrVLAInvalidStreamCount is returned when RTP stream count is invalid
|
||||
ErrVLAInvalidStreamID = errors.New("invalid RTP stream ID in VLA") // ErrVLAInvalidStreamID is returned when RTP stream ID is invalid
|
||||
ErrVLAInvalidSpatialID = errors.New("invalid spatial ID in VLA") // ErrVLAInvalidSpatialID is returned when spatial ID is invalid
|
||||
ErrVLADuplicateSpatialID = errors.New("duplicate spatial ID in VLA") // ErrVLADuplicateSpatialID is returned when spatial ID is invalid
|
||||
ErrVLAInvalidTemporalLayer = errors.New("invalid temporal layer in VLA") // ErrVLAInvalidTemporalLayer is returned when temporal layer is invalid
|
||||
)
|
||||
|
||||
// SpatialLayer is a spatial layer in VLA.
|
||||
type SpatialLayer struct {
|
||||
RTPStreamID int
|
||||
SpatialID int
|
||||
TargetBitrates []int // target bitrates per temporal layer
|
||||
|
||||
// Following members are valid only when HasResolutionAndFramerate is true
|
||||
Width int
|
||||
Height int
|
||||
Framerate int
|
||||
}
|
||||
|
||||
// VLA is a Video Layer Allocation (VLA) extension.
|
||||
// See https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/video-layers-allocation00
|
||||
type VLA struct {
|
||||
RTPStreamID int // 0-origin RTP stream ID (RID) this allocation is sent on (0..3)
|
||||
RTPStreamCount int // Number of RTP streams (1..4)
|
||||
ActiveSpatialLayer []SpatialLayer
|
||||
HasResolutionAndFramerate bool
|
||||
}
|
||||
|
||||
type vlaMarshalingContext struct {
|
||||
slMBs [4]uint8
|
||||
sls [4][4]*SpatialLayer
|
||||
commonSLBM uint8
|
||||
encodedTargetBitrates [][]byte
|
||||
requiredLen int
|
||||
}
|
||||
|
||||
func (v VLA) preprocessForMashaling(ctx *vlaMarshalingContext) error {
|
||||
for i := 0; i < len(v.ActiveSpatialLayer); i++ {
|
||||
sl := v.ActiveSpatialLayer[i]
|
||||
if sl.RTPStreamID < 0 || sl.RTPStreamID >= v.RTPStreamCount {
|
||||
return fmt.Errorf("invalid RTP streamID %d:%w", sl.RTPStreamID, ErrVLAInvalidStreamID)
|
||||
}
|
||||
if sl.SpatialID < 0 || sl.SpatialID >= 4 {
|
||||
return fmt.Errorf("invalid spatial ID %d: %w", sl.SpatialID, ErrVLAInvalidSpatialID)
|
||||
}
|
||||
if len(sl.TargetBitrates) == 0 || len(sl.TargetBitrates) > 4 {
|
||||
return fmt.Errorf("invalid temporal layer count %d: %w", len(sl.TargetBitrates), ErrVLAInvalidTemporalLayer)
|
||||
}
|
||||
ctx.slMBs[sl.RTPStreamID] |= 1 << sl.SpatialID
|
||||
if ctx.sls[sl.RTPStreamID][sl.SpatialID] != nil {
|
||||
return fmt.Errorf("duplicate spatial layer: %w", ErrVLADuplicateSpatialID)
|
||||
}
|
||||
ctx.sls[sl.RTPStreamID][sl.SpatialID] = &sl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v VLA) encodeTargetBitrates(ctx *vlaMarshalingContext) {
|
||||
for rtpStreamID := 0; rtpStreamID < v.RTPStreamCount; rtpStreamID++ {
|
||||
for spatialID := 0; spatialID < 4; spatialID++ {
|
||||
if sl := ctx.sls[rtpStreamID][spatialID]; sl != nil {
|
||||
for _, kbps := range sl.TargetBitrates {
|
||||
leb128 := obu.WriteToLeb128(uint(kbps))
|
||||
ctx.encodedTargetBitrates = append(ctx.encodedTargetBitrates, leb128)
|
||||
ctx.requiredLen += len(leb128)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v VLA) analyzeVLAForMarshaling() (*vlaMarshalingContext, error) {
|
||||
// Validate RTPStreamCount
|
||||
if v.RTPStreamCount <= 0 || v.RTPStreamCount > 4 {
|
||||
return nil, ErrVLAInvalidStreamCount
|
||||
}
|
||||
// Validate RTPStreamID
|
||||
if v.RTPStreamID < 0 || v.RTPStreamID >= v.RTPStreamCount {
|
||||
return nil, ErrVLAInvalidStreamID
|
||||
}
|
||||
|
||||
ctx := &vlaMarshalingContext{}
|
||||
err := v.preprocessForMashaling(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.commonSLBM = commonSLBMValues(ctx.slMBs[:])
|
||||
|
||||
// RID, NS, sl_bm fields
|
||||
if ctx.commonSLBM != 0 {
|
||||
ctx.requiredLen = 1
|
||||
} else {
|
||||
ctx.requiredLen = 3
|
||||
}
|
||||
|
||||
// #tl fields
|
||||
ctx.requiredLen += (len(v.ActiveSpatialLayer)-1)/4 + 1
|
||||
|
||||
v.encodeTargetBitrates(ctx)
|
||||
|
||||
if v.HasResolutionAndFramerate {
|
||||
ctx.requiredLen += len(v.ActiveSpatialLayer) * 5
|
||||
}
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Marshal encodes VLA into a byte slice.
|
||||
func (v VLA) Marshal() ([]byte, error) {
|
||||
ctx, err := v.analyzeVLAForMarshaling()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload := make([]byte, ctx.requiredLen)
|
||||
offset := 0
|
||||
|
||||
// RID, NS, sl_bm fields
|
||||
payload[offset] = byte(v.RTPStreamID<<6) | byte(v.RTPStreamCount-1)<<4 | ctx.commonSLBM
|
||||
|
||||
if ctx.commonSLBM == 0 {
|
||||
offset++
|
||||
for streamID := 0; streamID < v.RTPStreamCount; streamID++ {
|
||||
if streamID%2 == 0 {
|
||||
payload[offset+streamID/2] |= ctx.slMBs[streamID] << 4
|
||||
} else {
|
||||
payload[offset+streamID/2] |= ctx.slMBs[streamID]
|
||||
}
|
||||
}
|
||||
offset += (v.RTPStreamCount - 1) / 2
|
||||
}
|
||||
|
||||
// #tl fields
|
||||
offset++
|
||||
var temporalLayerIndex int
|
||||
for rtpStreamID := 0; rtpStreamID < v.RTPStreamCount; rtpStreamID++ {
|
||||
for spatialID := 0; spatialID < 4; spatialID++ {
|
||||
if sl := ctx.sls[rtpStreamID][spatialID]; sl != nil {
|
||||
if temporalLayerIndex >= 4 {
|
||||
temporalLayerIndex = 0
|
||||
offset++
|
||||
}
|
||||
payload[offset] |= byte(len(sl.TargetBitrates)-1) << (2 * (3 - temporalLayerIndex))
|
||||
temporalLayerIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Target bitrate fields
|
||||
offset++
|
||||
for _, encodedKbps := range ctx.encodedTargetBitrates {
|
||||
encodedSize := len(encodedKbps)
|
||||
copy(payload[offset:], encodedKbps)
|
||||
offset += encodedSize
|
||||
}
|
||||
|
||||
// Resolution & framerate fields
|
||||
if v.HasResolutionAndFramerate {
|
||||
for _, sl := range v.ActiveSpatialLayer {
|
||||
binary.BigEndian.PutUint16(payload[offset+0:], uint16(sl.Width-1))
|
||||
binary.BigEndian.PutUint16(payload[offset+2:], uint16(sl.Height-1))
|
||||
payload[offset+4] = byte(sl.Framerate)
|
||||
offset += 5
|
||||
}
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func commonSLBMValues(slMBs []uint8) uint8 {
|
||||
var common uint8
|
||||
for i := 0; i < len(slMBs); i++ {
|
||||
if slMBs[i] == 0 {
|
||||
continue
|
||||
}
|
||||
if common == 0 {
|
||||
common = slMBs[i]
|
||||
continue
|
||||
}
|
||||
if slMBs[i] != common {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return common
|
||||
}
|
||||
|
||||
type vlaUnmarshalingContext struct {
|
||||
payload []byte
|
||||
offset int
|
||||
slBMField uint8
|
||||
slBMs [4]uint8
|
||||
}
|
||||
|
||||
func (ctx *vlaUnmarshalingContext) checkRemainingLen(requiredLen int) bool {
|
||||
return len(ctx.payload)-ctx.offset >= requiredLen
|
||||
}
|
||||
|
||||
func (v *VLA) unmarshalSpatialLayers(ctx *vlaUnmarshalingContext) error {
|
||||
if !ctx.checkRemainingLen(1) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
v.RTPStreamID = int(ctx.payload[ctx.offset] >> 6 & 0b11)
|
||||
v.RTPStreamCount = int(ctx.payload[ctx.offset]>>4&0b11) + 1
|
||||
|
||||
// sl_bm fields
|
||||
ctx.slBMField = ctx.payload[ctx.offset] & 0b1111
|
||||
ctx.offset++
|
||||
|
||||
if ctx.slBMField != 0 {
|
||||
for streamID := 0; streamID < v.RTPStreamCount; streamID++ {
|
||||
ctx.slBMs[streamID] = ctx.slBMField
|
||||
}
|
||||
} else {
|
||||
if !ctx.checkRemainingLen((v.RTPStreamCount-1)/2 + 1) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
// slX_bm fields
|
||||
for streamID := 0; streamID < v.RTPStreamCount; streamID++ {
|
||||
var bm uint8
|
||||
if streamID%2 == 0 {
|
||||
bm = ctx.payload[ctx.offset+streamID/2] >> 4 & 0b1111
|
||||
} else {
|
||||
bm = ctx.payload[ctx.offset+streamID/2] & 0b1111
|
||||
}
|
||||
ctx.slBMs[streamID] = bm
|
||||
}
|
||||
ctx.offset += 1 + (v.RTPStreamCount-1)/2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VLA) unmarshalTemporalLayers(ctx *vlaUnmarshalingContext) error {
|
||||
if !ctx.checkRemainingLen(1) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
|
||||
var temporalLayerIndex int
|
||||
for streamID := 0; streamID < v.RTPStreamCount; streamID++ {
|
||||
for spatialID := 0; spatialID < 4; spatialID++ {
|
||||
if ctx.slBMs[streamID]&(1<<spatialID) == 0 {
|
||||
continue
|
||||
}
|
||||
if temporalLayerIndex >= 4 {
|
||||
temporalLayerIndex = 0
|
||||
ctx.offset++
|
||||
if !ctx.checkRemainingLen(1) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
}
|
||||
tlCount := int(ctx.payload[ctx.offset]>>(2*(3-temporalLayerIndex))&0b11) + 1
|
||||
temporalLayerIndex++
|
||||
sl := SpatialLayer{
|
||||
RTPStreamID: streamID,
|
||||
SpatialID: spatialID,
|
||||
TargetBitrates: make([]int, tlCount),
|
||||
}
|
||||
v.ActiveSpatialLayer = append(v.ActiveSpatialLayer, sl)
|
||||
}
|
||||
}
|
||||
ctx.offset++
|
||||
|
||||
// target bitrates
|
||||
for i, sl := range v.ActiveSpatialLayer {
|
||||
for j := range sl.TargetBitrates {
|
||||
kbps, n, err := obu.ReadLeb128(ctx.payload[ctx.offset:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ctx.checkRemainingLen(int(n)) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
v.ActiveSpatialLayer[i].TargetBitrates[j] = int(kbps)
|
||||
ctx.offset += int(n)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VLA) unmarshalResolutionAndFramerate(ctx *vlaUnmarshalingContext) error {
|
||||
if !ctx.checkRemainingLen(len(v.ActiveSpatialLayer) * 5) {
|
||||
return fmt.Errorf("failed to unmarshal VLA (offset=%d): %w", ctx.offset, ErrVLATooShort)
|
||||
}
|
||||
|
||||
v.HasResolutionAndFramerate = true
|
||||
|
||||
for i := range v.ActiveSpatialLayer {
|
||||
v.ActiveSpatialLayer[i].Width = int(binary.BigEndian.Uint16(ctx.payload[ctx.offset+0:])) + 1
|
||||
v.ActiveSpatialLayer[i].Height = int(binary.BigEndian.Uint16(ctx.payload[ctx.offset+2:])) + 1
|
||||
v.ActiveSpatialLayer[i].Framerate = int(ctx.payload[ctx.offset+4])
|
||||
ctx.offset += 5
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes VLA from a byte slice.
|
||||
func (v *VLA) Unmarshal(payload []byte) (int, error) {
|
||||
ctx := &vlaUnmarshalingContext{
|
||||
payload: payload,
|
||||
}
|
||||
|
||||
err := v.unmarshalSpatialLayers(ctx)
|
||||
if err != nil {
|
||||
return ctx.offset, err
|
||||
}
|
||||
|
||||
// #tl fields (build the list ActiveSpatialLayer at the same time)
|
||||
err = v.unmarshalTemporalLayers(ctx)
|
||||
if err != nil {
|
||||
return ctx.offset, err
|
||||
}
|
||||
|
||||
if len(ctx.payload) == ctx.offset {
|
||||
return ctx.offset, nil
|
||||
}
|
||||
|
||||
// resolution & framerate (optional)
|
||||
err = v.unmarshalResolutionAndFramerate(ctx)
|
||||
if err != nil {
|
||||
return ctx.offset, err
|
||||
}
|
||||
|
||||
return ctx.offset, nil
|
||||
}
|
||||
|
||||
// String makes VLA printable.
|
||||
func (v VLA) String() string {
|
||||
out := fmt.Sprintf("RID:%d,RTPStreamCount:%d", v.RTPStreamID, v.RTPStreamCount)
|
||||
var slOut []string
|
||||
for _, sl := range v.ActiveSpatialLayer {
|
||||
out2 := fmt.Sprintf("RTPStreamID:%d", sl.RTPStreamID)
|
||||
out2 += fmt.Sprintf(",TargetBitrates:%v", sl.TargetBitrates)
|
||||
if v.HasResolutionAndFramerate {
|
||||
out2 += fmt.Sprintf(",Resolution:(%d,%d)", sl.Width, sl.Height)
|
||||
out2 += fmt.Sprintf(",Framerate:%d", sl.Framerate)
|
||||
}
|
||||
slOut = append(slOut, out2)
|
||||
}
|
||||
out += fmt.Sprintf(",ActiveSpatialLayers:{%s}", strings.Join(slOut, ","))
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user