mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
For regression test, add srs-bench to 3rdparty
This commit is contained in:
parent
de87dd427d
commit
876210f6c9
1158 changed files with 256967 additions and 3 deletions
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.gitignore
generated
vendored
Normal file
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
### JetBrains IDE ###
|
||||
#####################
|
||||
.idea/
|
||||
|
||||
### Emacs Temporary Files ###
|
||||
#############################
|
||||
*~
|
||||
|
||||
### Folders ###
|
||||
###############
|
||||
bin/
|
||||
vendor/
|
||||
node_modules/
|
||||
|
||||
### Files ###
|
||||
#############
|
||||
*.ivf
|
||||
*.ogg
|
||||
tags
|
||||
cover.out
|
||||
*.sw[poe]
|
||||
*.wasm
|
||||
examples/sfu-ws/cert.pem
|
||||
examples/sfu-ws/key.pem
|
89
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml
generated
vendored
Normal file
89
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/.golangci.yml
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
misspell:
|
||||
locale: US
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
|
||||
- bodyclose # checks whether HTTP response body is closed successfully
|
||||
- deadcode # Finds unused code
|
||||
- depguard # Go linter that checks if package imports are in a list of acceptable packages
|
||||
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
|
||||
- dupl # Tool for code clone detection
|
||||
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
|
||||
- exhaustive # check exhaustiveness of enum switch statements
|
||||
- exportloopref # checks for pointers to enclosing loop variables
|
||||
- gci # Gci control golang package import order and make it always deterministic.
|
||||
- gochecknoglobals # Checks that no globals are present in Go code
|
||||
- gochecknoinits # Checks that no init functions are present in Go code
|
||||
- gocognit # Computes and checks the cognitive complexity of functions
|
||||
- goconst # Finds repeated strings that could be replaced by a constant
|
||||
- gocritic # The most opinionated Go source code linter
|
||||
- godox # Tool for detection of FIXME, TODO and other comment keywords
|
||||
- goerr113 # Golang linter to check the errors handling expressions
|
||||
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
|
||||
- gofumpt # Gofumpt checks whether code was gofumpt-ed.
|
||||
- goheader # Checks is file header matches to pattern
|
||||
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
|
||||
- golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
||||
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
|
||||
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
|
||||
- gosec # Inspects source code for security problems
|
||||
- gosimple # Linter for Go source code that specializes in simplifying a code
|
||||
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- ineffassign # Detects when assignments to existing variables are not used
|
||||
- misspell # Finds commonly misspelled English words in comments
|
||||
- nakedret # Finds naked returns in functions greater than a specified function length
|
||||
- noctx # noctx finds sending http request without context.Context
|
||||
- scopelint # Scopelint checks for unpinned variables in go programs
|
||||
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
|
||||
- structcheck # Finds unused struct fields
|
||||
- stylecheck # Stylecheck is a replacement for golint
|
||||
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
|
||||
- unconvert # Remove unnecessary type conversions
|
||||
- unparam # Reports unused function parameters
|
||||
- unused # Checks Go code for unused constants, variables, functions and types
|
||||
- varcheck # Finds unused global variables and constants
|
||||
- whitespace # Tool for detection of leading and trailing whitespace
|
||||
disable:
|
||||
- funlen # Tool for detection of long functions
|
||||
- gocyclo # Computes and checks the cyclomatic complexity of functions
|
||||
- godot # Check if comments end in a period
|
||||
- gomnd # An analyzer to detect magic numbers.
|
||||
- lll # Reports long lines
|
||||
- maligned # Tool to detect Go structs that would take less memory if their fields were sorted
|
||||
- nestif # Reports deeply nested if statements
|
||||
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives
|
||||
- prealloc # Finds slice declarations that could potentially be preallocated
|
||||
- rowserrcheck # checks whether Err of rows is checked successfully
|
||||
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
|
||||
- testpackage # linter that makes you use a separate _test package
|
||||
- wsl # Whitespace Linter - Forces you to use empty lines!
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
# Allow complex tests, better to be self contained
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocognit
|
||||
|
||||
# Allow complex main function in examples
|
||||
- path: examples
|
||||
text: "of func `main` is high"
|
||||
linters:
|
||||
- gocognit
|
||||
|
||||
run:
|
||||
skip-dirs-use-default: false
|
21
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/LICENSE
generated
vendored
Normal file
21
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
59
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md
generated
vendored
Normal file
59
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/README.md
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
<h1 align="center">
|
||||
<br>
|
||||
Pion SDP
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A Go implementation of the SDP</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-sdp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SDP"></a>
|
||||
<a href="https://sourcegraph.com/github.com/pion/sdp?badge"><img src="https://sourcegraph.com/github.com/pion/sdp/-/badge.svg" alt="Sourcegraph Widget"></a>
|
||||
<a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/pion/sdp"><img src="https://travis-ci.org/pion/sdp.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://pkg.go.dev/github.com/pion/sdp/v2"><img src="https://godoc.org/github.com/pion/sdp?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://codecov.io/gh/pion/sdp"><img src="https://codecov.io/gh/pion/sdp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/sdp"><img src="https://goreportcard.com/badge/github.com/pion/sdp" alt="Go Report Card"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
|
||||
|
||||
### Roadmap
|
||||
The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||
|
||||
### Community
|
||||
Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
|
||||
|
||||
We are always looking to support **your projects**. Please reach out if you have something to build!
|
||||
|
||||
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
|
||||
|
||||
### Contributing
|
||||
Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
|
||||
|
||||
* [John Bradley](https://github.com/kc5nra) - *Original Author*
|
||||
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
|
||||
* [Michiel De Backker](https://github.com/backkem) - *Public API, Initialization*
|
||||
* [Konstantin Itskov](https://github.com/trivigy) - *Fix documentation*
|
||||
* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
|
||||
* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
|
||||
* [Michael MacDonald](https://github.com/mjmac)
|
||||
* [Max Hawkins](https://github.com/maxhawkins)
|
||||
* [mchlrhw](https://github.com/mchlrhw)
|
||||
* [Hugo Arregui](https://github.com/hugoArregui)
|
||||
* [Guilherme Souza](https://github.com/gqgs)
|
||||
* [adwpc](https://github.com/adwpc) - *extmap add transport-cc*
|
||||
* [Atsushi Watanabe](https://github.com/at-wat)
|
||||
* [Luke S](https://github.com/encounter)
|
||||
* [Jerko Steiner](https://github.com/jeremija)
|
||||
* [Roman Romanenko](https://github.com/r-novel)
|
||||
* [Jason Brady](https://github.com/jbrady42)
|
||||
* [Kory Miller](https://github.com/jbrady42/korymiller1489)
|
||||
* [ZHENK](https://github.com/scorpionknifes)
|
||||
* [Tarrence van As](https://github.com/tarrencev)
|
||||
* [Maxim Oransky](https://github.com/sdfsdhgjkbmnmxc)
|
||||
* [Graham King](https://github.com/grahamking/)
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
231
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/base_lexer.go
generated
vendored
Normal file
231
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/base_lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
var errDocumentStart = errors.New("already on document start")
|
||||
|
||||
type syntaxError struct {
|
||||
s string
|
||||
i int
|
||||
}
|
||||
|
||||
func (e syntaxError) Error() string {
|
||||
if e.i < 0 {
|
||||
e.i = 0
|
||||
}
|
||||
head, middle, tail := e.s[:e.i], e.s[e.i:e.i+1], e.s[e.i+1:]
|
||||
return fmt.Sprintf("%s --> %s <-- %s", head, middle, tail)
|
||||
}
|
||||
|
||||
type baseLexer struct {
|
||||
value []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
func (l baseLexer) syntaxError() error {
|
||||
return syntaxError{s: string(l.value), i: l.pos - 1}
|
||||
}
|
||||
|
||||
func (l *baseLexer) unreadByte() error {
|
||||
if l.pos <= 0 {
|
||||
return errDocumentStart
|
||||
}
|
||||
l.pos--
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *baseLexer) readByte() (byte, error) {
|
||||
if l.pos >= len(l.value) {
|
||||
return byte(0), io.EOF
|
||||
}
|
||||
ch := l.value[l.pos]
|
||||
l.pos++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (l *baseLexer) nextLine() error {
|
||||
for {
|
||||
ch, err := l.readByte()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isNewline(ch) {
|
||||
return l.unreadByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *baseLexer) readWhitespace() error {
|
||||
for {
|
||||
ch, err := l.readByte()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isWhitespace(ch) {
|
||||
return l.unreadByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *baseLexer) readUint64Field() (i uint64, err error) {
|
||||
for {
|
||||
ch, err := l.readByte()
|
||||
if err == io.EOF && i > 0 {
|
||||
break
|
||||
} else if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
if isNewline(ch) {
|
||||
if err := l.unreadByte(); err != nil {
|
||||
return i, err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if isWhitespace(ch) {
|
||||
if err := l.readWhitespace(); err != nil {
|
||||
return i, err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case '0':
|
||||
i *= 10
|
||||
case '1':
|
||||
i = i*10 + 1
|
||||
case '2':
|
||||
i = i*10 + 2
|
||||
case '3':
|
||||
i = i*10 + 3
|
||||
case '4':
|
||||
i = i*10 + 4
|
||||
case '5':
|
||||
i = i*10 + 5
|
||||
case '6':
|
||||
i = i*10 + 6
|
||||
case '7':
|
||||
i = i*10 + 7
|
||||
case '8':
|
||||
i = i*10 + 8
|
||||
case '9':
|
||||
i = i*10 + 9
|
||||
default:
|
||||
return i, l.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Returns next field on this line or empty string if no more fields on line
|
||||
func (l *baseLexer) readField() (string, error) {
|
||||
start := l.pos
|
||||
stop := start
|
||||
for {
|
||||
stop = l.pos
|
||||
ch, err := l.readByte()
|
||||
if err == io.EOF && stop > start {
|
||||
break
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if isNewline(ch) {
|
||||
if err := l.unreadByte(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if isWhitespace(ch) {
|
||||
if err := l.readWhitespace(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(l.value[start:stop]), nil
|
||||
}
|
||||
|
||||
// Returns symbols until line end
|
||||
func (l *baseLexer) readLine() (string, error) {
|
||||
start := l.pos
|
||||
trim := 1
|
||||
for {
|
||||
ch, err := l.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ch == '\r' {
|
||||
trim++
|
||||
}
|
||||
if ch == '\n' {
|
||||
return string(l.value[start : l.pos-trim]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *baseLexer) readString(until byte) (string, error) {
|
||||
start := l.pos
|
||||
for {
|
||||
ch, err := l.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ch == until {
|
||||
return string(l.value[start:l.pos]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *baseLexer) readType() (string, error) {
|
||||
for {
|
||||
b, err := l.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if isNewline(b) {
|
||||
continue
|
||||
}
|
||||
|
||||
err = l.unreadByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
key, err := l.readString('=')
|
||||
if err != nil {
|
||||
return key, err
|
||||
}
|
||||
|
||||
if len(key) == 2 {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return key, l.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
func isNewline(ch byte) bool { return ch == '\n' || ch == '\r' }
|
||||
|
||||
func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' }
|
||||
|
||||
func anyOf(element string, data ...string) bool {
|
||||
for _, v := range data {
|
||||
if element == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/codecov.yml
generated
vendored
Normal file
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/codecov.yml
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
#
|
||||
# DO NOT EDIT THIS FILE
|
||||
#
|
||||
# It is automatically copied from https://github.com/pion/.goassets repository.
|
||||
#
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# Allow decreasing 2% of total coverage to avoid noise.
|
||||
threshold: 2%
|
||||
patch:
|
||||
default:
|
||||
target: 70%
|
||||
only_pulls: true
|
||||
|
||||
ignore:
|
||||
- "examples/*"
|
||||
- "examples/**/*"
|
110
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/common_description.go
generated
vendored
Normal file
110
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/common_description.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Information describes the "i=" field which provides textual information
|
||||
// about the session.
|
||||
type Information string
|
||||
|
||||
func (i Information) String() string {
|
||||
return string(i)
|
||||
}
|
||||
|
||||
// ConnectionInformation defines the representation for the "c=" field
|
||||
// containing connection data.
|
||||
type ConnectionInformation struct {
|
||||
NetworkType string
|
||||
AddressType string
|
||||
Address *Address
|
||||
}
|
||||
|
||||
func (c ConnectionInformation) String() string {
|
||||
parts := []string{c.NetworkType, c.AddressType}
|
||||
if c.Address != nil && c.Address.String() != "" {
|
||||
parts = append(parts, c.Address.String())
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// Address desribes a structured address token from within the "c=" field.
|
||||
type Address struct {
|
||||
Address string
|
||||
TTL *int
|
||||
Range *int
|
||||
}
|
||||
|
||||
func (c *Address) String() string {
|
||||
var parts []string
|
||||
parts = append(parts, c.Address)
|
||||
if c.TTL != nil {
|
||||
parts = append(parts, strconv.Itoa(*c.TTL))
|
||||
}
|
||||
|
||||
if c.Range != nil {
|
||||
parts = append(parts, strconv.Itoa(*c.Range))
|
||||
}
|
||||
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
// Bandwidth describes an optional field which denotes the proposed bandwidth
|
||||
// to be used by the session or media.
|
||||
type Bandwidth struct {
|
||||
Experimental bool
|
||||
Type string
|
||||
Bandwidth uint64
|
||||
}
|
||||
|
||||
func (b Bandwidth) String() string {
|
||||
var output string
|
||||
if b.Experimental {
|
||||
output += "X-"
|
||||
}
|
||||
output += b.Type + ":" + strconv.FormatUint(b.Bandwidth, 10)
|
||||
return output
|
||||
}
|
||||
|
||||
// EncryptionKey describes the "k=" which conveys encryption key information.
|
||||
type EncryptionKey string
|
||||
|
||||
func (s EncryptionKey) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// Attribute describes the "a=" field which represents the primary means for
|
||||
// extending SDP.
|
||||
type Attribute struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewPropertyAttribute constructs a new attribute
|
||||
func NewPropertyAttribute(key string) Attribute {
|
||||
return Attribute{
|
||||
Key: key,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAttribute constructs a new attribute
|
||||
func NewAttribute(key, value string) Attribute {
|
||||
return Attribute{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (a Attribute) String() string {
|
||||
output := a.Key
|
||||
if len(a.Value) > 0 {
|
||||
output += ":" + a.Value
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// IsICECandidate returns true if the attribute key equals "candidate".
|
||||
func (a Attribute) IsICECandidate() bool {
|
||||
return a.Key == "candidate"
|
||||
}
|
59
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/direction.go
generated
vendored
Normal file
59
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/direction.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package sdp
|
||||
|
||||
import "errors"
|
||||
|
||||
// Direction is a marker for transmission directon of an endpoint
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
// DirectionSendRecv is for bidirectional communication
|
||||
DirectionSendRecv Direction = iota + 1
|
||||
// DirectionSendOnly is for outgoing communication
|
||||
DirectionSendOnly
|
||||
// DirectionRecvOnly is for incoming communication
|
||||
DirectionRecvOnly
|
||||
// DirectionInactive is for no communication
|
||||
DirectionInactive
|
||||
)
|
||||
|
||||
const (
|
||||
directionSendRecvStr = "sendrecv"
|
||||
directionSendOnlyStr = "sendonly"
|
||||
directionRecvOnlyStr = "recvonly"
|
||||
directionInactiveStr = "inactive"
|
||||
directionUnknownStr = ""
|
||||
)
|
||||
|
||||
var errDirectionString = errors.New("invalid direction string")
|
||||
|
||||
// NewDirection defines a procedure for creating a new direction from a raw
|
||||
// string.
|
||||
func NewDirection(raw string) (Direction, error) {
|
||||
switch raw {
|
||||
case directionSendRecvStr:
|
||||
return DirectionSendRecv, nil
|
||||
case directionSendOnlyStr:
|
||||
return DirectionSendOnly, nil
|
||||
case directionRecvOnlyStr:
|
||||
return DirectionRecvOnly, nil
|
||||
case directionInactiveStr:
|
||||
return DirectionInactive, nil
|
||||
default:
|
||||
return Direction(unknown), errDirectionString
|
||||
}
|
||||
}
|
||||
|
||||
func (t Direction) String() string {
|
||||
switch t {
|
||||
case DirectionSendRecv:
|
||||
return directionSendRecvStr
|
||||
case DirectionSendOnly:
|
||||
return directionSendOnlyStr
|
||||
case DirectionRecvOnly:
|
||||
return directionRecvOnlyStr
|
||||
case DirectionInactive:
|
||||
return directionInactiveStr
|
||||
default:
|
||||
return directionUnknownStr
|
||||
}
|
||||
}
|
108
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/extmap.go
generated
vendored
Normal file
108
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/extmap.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Default ext values
|
||||
const (
|
||||
DefExtMapValueABSSendTime = 1
|
||||
DefExtMapValueTransportCC = 2
|
||||
DefExtMapValueSDESMid = 3
|
||||
DefExtMapValueSDESRTPStreamID = 4
|
||||
|
||||
ABSSendTimeURI = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
|
||||
TransportCCURI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
|
||||
SDESMidURI = "urn:ietf:params:rtp-hdrext:sdes:mid"
|
||||
SDESRTPStreamIDURI = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"
|
||||
AudioLevelURI = "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
|
||||
)
|
||||
|
||||
// ExtMap represents the activation of a single RTP header extension
|
||||
type ExtMap struct {
|
||||
Value int
|
||||
Direction Direction
|
||||
URI *url.URL
|
||||
ExtAttr *string
|
||||
}
|
||||
|
||||
// Clone converts this object to an Attribute
|
||||
func (e *ExtMap) Clone() Attribute {
|
||||
return Attribute{Key: "extmap", Value: e.string()}
|
||||
}
|
||||
|
||||
// Unmarshal creates an Extmap from a string
|
||||
func (e *ExtMap) Unmarshal(raw string) error {
|
||||
parts := strings.SplitN(raw, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("%w: %v", errSyntaxError, raw)
|
||||
}
|
||||
|
||||
fields := strings.Fields(parts[1])
|
||||
if len(fields) < 2 {
|
||||
return fmt.Errorf("%w: %v", errSyntaxError, raw)
|
||||
}
|
||||
|
||||
valdir := strings.Split(fields[0], "/")
|
||||
value, err := strconv.ParseInt(valdir[0], 10, 64)
|
||||
if (value < 1) || (value > 246) {
|
||||
return fmt.Errorf("%w: %v -- extmap key must be in the range 1-256", errSyntaxError, valdir[0])
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", errSyntaxError, valdir[0])
|
||||
}
|
||||
|
||||
var direction Direction
|
||||
if len(valdir) == 2 {
|
||||
direction, err = NewDirection(valdir[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
uri, err := url.Parse(fields[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fields) == 3 {
|
||||
tmp := fields[2]
|
||||
e.ExtAttr = &tmp
|
||||
}
|
||||
|
||||
e.Value = int(value)
|
||||
e.Direction = direction
|
||||
e.URI = uri
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal creates a string from an ExtMap
|
||||
func (e *ExtMap) Marshal() string {
|
||||
return e.Name() + ":" + e.string()
|
||||
}
|
||||
|
||||
func (e *ExtMap) string() string {
|
||||
output := fmt.Sprintf("%d", e.Value)
|
||||
dirstring := e.Direction.String()
|
||||
if dirstring != directionUnknownStr {
|
||||
output += "/" + dirstring
|
||||
}
|
||||
|
||||
if e.URI != nil {
|
||||
output += " " + e.URI.String()
|
||||
}
|
||||
|
||||
if e.ExtAttr != nil {
|
||||
output += " " + *e.ExtAttr
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// Name returns the constant name of this object
|
||||
func (e *ExtMap) Name() string {
|
||||
return "extmap"
|
||||
}
|
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/fuzz.go
generated
vendored
Normal file
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/fuzz.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// +build gofuzz
|
||||
|
||||
package sdp
|
||||
|
||||
// Fuzz implements a randomized fuzz test of the sdp
|
||||
// parser using go-fuzz.
|
||||
//
|
||||
// To run the fuzzer, first download go-fuzz:
|
||||
// `go get github.com/dvyukov/go-fuzz/...`
|
||||
//
|
||||
// Then build the testing package:
|
||||
// `go-fuzz-build`
|
||||
//
|
||||
// And run the fuzzer on the corpus:
|
||||
// `go-fuzz`
|
||||
func Fuzz(data []byte) int {
|
||||
// Check that unmarshalling any byte slice does not panic.
|
||||
var sd SessionDescription
|
||||
if err := sd.Unmarshal(data); err != nil {
|
||||
return 0
|
||||
}
|
||||
// Check that we can marshal anything we unmarshalled.
|
||||
_, err := sd.Marshal()
|
||||
if err != nil {
|
||||
panic("failed to marshal") // nolint
|
||||
}
|
||||
// It'd be nice to check that if we round trip Marshal then Unmarshal,
|
||||
// we get the original back. Right now, though, we frequently don't,
|
||||
// and we'd need to fix that first.
|
||||
return 1
|
||||
}
|
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/go.mod
generated
vendored
Normal file
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
module github.com/pion/sdp/v3
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/pion/randutil v0.1.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
)
|
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/go.sum
generated
vendored
Normal file
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
205
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go
generated
vendored
Normal file
205
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/jsep.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constants for SDP attributes used in JSEP
|
||||
const (
|
||||
AttrKeyCandidate = "candidate"
|
||||
AttrKeyEndOfCandidates = "end-of-candidates"
|
||||
AttrKeyIdentity = "identity"
|
||||
AttrKeyGroup = "group"
|
||||
AttrKeySSRC = "ssrc"
|
||||
AttrKeySSRCGroup = "ssrc-group"
|
||||
AttrKeyMsid = "msid"
|
||||
AttrKeyMsidSemantic = "msid-semantic"
|
||||
AttrKeyConnectionSetup = "setup"
|
||||
AttrKeyMID = "mid"
|
||||
AttrKeyICELite = "ice-lite"
|
||||
AttrKeyRTCPMux = "rtcp-mux"
|
||||
AttrKeyRTCPRsize = "rtcp-rsize"
|
||||
AttrKeyInactive = "inactive"
|
||||
AttrKeyRecvOnly = "recvonly"
|
||||
AttrKeySendOnly = "sendonly"
|
||||
AttrKeySendRecv = "sendrecv"
|
||||
AttrKeyExtMap = "extmap"
|
||||
)
|
||||
|
||||
// Constants for semantic tokens used in JSEP
|
||||
const (
|
||||
SemanticTokenLipSynchronization = "LS"
|
||||
SemanticTokenFlowIdentification = "FID"
|
||||
SemanticTokenForwardErrorCorrection = "FEC"
|
||||
SemanticTokenWebRTCMediaStreams = "WMS"
|
||||
)
|
||||
|
||||
// Constants for extmap key
|
||||
const (
|
||||
ExtMapValueTransportCC = 3
|
||||
)
|
||||
|
||||
func extMapURI() map[int]string {
|
||||
return map[int]string{
|
||||
ExtMapValueTransportCC: "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
|
||||
}
|
||||
}
|
||||
|
||||
// API to match draft-ietf-rtcweb-jsep
|
||||
// Move to webrtc or its own package?
|
||||
|
||||
// NewJSEPSessionDescription creates a new SessionDescription with
|
||||
// some settings that are required by the JSEP spec.
|
||||
//
|
||||
// Note: Since v2.4.0, session ID has been fixed to use crypto random according to
|
||||
// JSEP spec, so that NewJSEPSessionDescription now returns error as a second
|
||||
// return value.
|
||||
func NewJSEPSessionDescription(identity bool) (*SessionDescription, error) {
|
||||
sid, err := newSessionID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &SessionDescription{
|
||||
Version: 0,
|
||||
Origin: Origin{
|
||||
Username: "-",
|
||||
SessionID: sid,
|
||||
SessionVersion: uint64(time.Now().Unix()),
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
UnicastAddress: "0.0.0.0",
|
||||
},
|
||||
SessionName: "-",
|
||||
TimeDescriptions: []TimeDescription{
|
||||
{
|
||||
Timing: Timing{
|
||||
StartTime: 0,
|
||||
StopTime: 0,
|
||||
},
|
||||
RepeatTimes: nil,
|
||||
},
|
||||
},
|
||||
Attributes: []Attribute{
|
||||
// "Attribute(ice-options:trickle)", // TODO: implement trickle ICE
|
||||
},
|
||||
}
|
||||
|
||||
if identity {
|
||||
d.WithPropertyAttribute(AttrKeyIdentity)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// WithPropertyAttribute adds a property attribute 'a=key' to the session description
|
||||
func (s *SessionDescription) WithPropertyAttribute(key string) *SessionDescription {
|
||||
s.Attributes = append(s.Attributes, NewPropertyAttribute(key))
|
||||
return s
|
||||
}
|
||||
|
||||
// WithValueAttribute adds a value attribute 'a=key:value' to the session description
|
||||
func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescription {
|
||||
s.Attributes = append(s.Attributes, NewAttribute(key, value))
|
||||
return s
|
||||
}
|
||||
|
||||
// WithFingerprint adds a fingerprint to the session description
|
||||
func (s *SessionDescription) WithFingerprint(algorithm, value string) *SessionDescription {
|
||||
return s.WithValueAttribute("fingerprint", algorithm+" "+value)
|
||||
}
|
||||
|
||||
// WithMedia adds a media description to the session description
|
||||
func (s *SessionDescription) WithMedia(md *MediaDescription) *SessionDescription {
|
||||
s.MediaDescriptions = append(s.MediaDescriptions, md)
|
||||
return s
|
||||
}
|
||||
|
||||
// NewJSEPMediaDescription creates a new MediaName with
|
||||
// some settings that are required by the JSEP spec.
|
||||
func NewJSEPMediaDescription(codecType string, codecPrefs []string) *MediaDescription {
|
||||
return &MediaDescription{
|
||||
MediaName: MediaName{
|
||||
Media: codecType,
|
||||
Port: RangedPort{Value: 9},
|
||||
Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
|
||||
},
|
||||
ConnectionInformation: &ConnectionInformation{
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
Address: &Address{
|
||||
Address: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WithPropertyAttribute adds a property attribute 'a=key' to the media description
|
||||
func (d *MediaDescription) WithPropertyAttribute(key string) *MediaDescription {
|
||||
d.Attributes = append(d.Attributes, NewPropertyAttribute(key))
|
||||
return d
|
||||
}
|
||||
|
||||
// WithValueAttribute adds a value attribute 'a=key:value' to the media description
|
||||
func (d *MediaDescription) WithValueAttribute(key, value string) *MediaDescription {
|
||||
d.Attributes = append(d.Attributes, NewAttribute(key, value))
|
||||
return d
|
||||
}
|
||||
|
||||
// WithFingerprint adds a fingerprint to the media description
|
||||
func (d *MediaDescription) WithFingerprint(algorithm, value string) *MediaDescription {
|
||||
return d.WithValueAttribute("fingerprint", algorithm+" "+value)
|
||||
}
|
||||
|
||||
// WithICECredentials adds ICE credentials to the media description
|
||||
func (d *MediaDescription) WithICECredentials(username, password string) *MediaDescription {
|
||||
return d.
|
||||
WithValueAttribute("ice-ufrag", username).
|
||||
WithValueAttribute("ice-pwd", password)
|
||||
}
|
||||
|
||||
// WithCodec adds codec information to the media description
|
||||
func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate uint32, channels uint16, fmtp string) *MediaDescription {
|
||||
d.MediaName.Formats = append(d.MediaName.Formats, strconv.Itoa(int(payloadType)))
|
||||
rtpmap := fmt.Sprintf("%d %s/%d", payloadType, name, clockrate)
|
||||
if channels > 0 {
|
||||
rtpmap += fmt.Sprintf("/%d", channels)
|
||||
}
|
||||
d.WithValueAttribute("rtpmap", rtpmap)
|
||||
if fmtp != "" {
|
||||
d.WithValueAttribute("fmtp", fmt.Sprintf("%d %s", payloadType, fmtp))
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// WithMediaSource adds media source information to the media description
|
||||
func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
|
||||
return d.
|
||||
WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not phased out?
|
||||
WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)).
|
||||
WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not phased out?
|
||||
WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label)) // Deprecated but not phased out?
|
||||
}
|
||||
|
||||
// WithCandidate adds an ICE candidate to the media description
|
||||
// Deprecated: use WithICECandidate instead
|
||||
func (d *MediaDescription) WithCandidate(value string) *MediaDescription {
|
||||
return d.WithValueAttribute("candidate", value)
|
||||
}
|
||||
|
||||
// WithExtMap adds an extmap to the media description
|
||||
func (d *MediaDescription) WithExtMap(e ExtMap) *MediaDescription {
|
||||
return d.WithPropertyAttribute(e.Marshal())
|
||||
}
|
||||
|
||||
// WithTransportCCExtMap adds an extmap to the media description
|
||||
func (d *MediaDescription) WithTransportCCExtMap() *MediaDescription {
|
||||
uri, _ := url.Parse(extMapURI()[ExtMapValueTransportCC])
|
||||
e := ExtMap{
|
||||
Value: ExtMapValueTransportCC,
|
||||
URI: uri,
|
||||
}
|
||||
return d.WithExtMap(e)
|
||||
}
|
136
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/marshal.go
generated
vendored
Normal file
136
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/marshal.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Marshal takes a SDP struct to text
|
||||
// https://tools.ietf.org/html/rfc4566#section-5
|
||||
// Session description
|
||||
// v= (protocol version)
|
||||
// o= (originator and session identifier)
|
||||
// s= (session name)
|
||||
// i=* (session information)
|
||||
// u=* (URI of description)
|
||||
// e=* (email address)
|
||||
// p=* (phone number)
|
||||
// c=* (connection information -- not required if included in
|
||||
// all media)
|
||||
// b=* (zero or more bandwidth information lines)
|
||||
// One or more time descriptions ("t=" and "r=" lines; see below)
|
||||
// z=* (time zone adjustments)
|
||||
// k=* (encryption key)
|
||||
// a=* (zero or more session attribute lines)
|
||||
// Zero or more media descriptions
|
||||
//
|
||||
// Time description
|
||||
// t= (time the session is active)
|
||||
// r=* (zero or more repeat times)
|
||||
//
|
||||
// Media description, if present
|
||||
// m= (media name and transport address)
|
||||
// i=* (media title)
|
||||
// c=* (connection information -- optional if included at
|
||||
// session level)
|
||||
// b=* (zero or more bandwidth information lines)
|
||||
// k=* (encryption key)
|
||||
// a=* (zero or more media attribute lines)
|
||||
func (s *SessionDescription) Marshal() ([]byte, error) {
|
||||
m := make(marshaller, 0, 1024)
|
||||
|
||||
m.addKeyValue("v=", s.Version.String())
|
||||
m.addKeyValue("o=", s.Origin.String())
|
||||
m.addKeyValue("s=", s.SessionName.String())
|
||||
|
||||
if s.SessionInformation != nil {
|
||||
m.addKeyValue("i=", s.SessionInformation.String())
|
||||
}
|
||||
|
||||
if s.URI != nil {
|
||||
m.addKeyValue("u=", s.URI.String())
|
||||
}
|
||||
|
||||
if s.EmailAddress != nil {
|
||||
m.addKeyValue("e=", s.EmailAddress.String())
|
||||
}
|
||||
|
||||
if s.PhoneNumber != nil {
|
||||
m.addKeyValue("p=", s.PhoneNumber.String())
|
||||
}
|
||||
|
||||
if s.ConnectionInformation != nil {
|
||||
m.addKeyValue("c=", s.ConnectionInformation.String())
|
||||
}
|
||||
|
||||
for _, b := range s.Bandwidth {
|
||||
m.addKeyValue("b=", b.String())
|
||||
}
|
||||
|
||||
for _, td := range s.TimeDescriptions {
|
||||
m.addKeyValue("t=", td.Timing.String())
|
||||
for _, r := range td.RepeatTimes {
|
||||
m.addKeyValue("r=", r.String())
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.TimeZones) > 0 {
|
||||
var b strings.Builder
|
||||
for i, z := range s.TimeZones {
|
||||
if i > 0 {
|
||||
b.WriteString(" ")
|
||||
}
|
||||
b.WriteString(z.String())
|
||||
}
|
||||
m.addKeyValue("z=", b.String())
|
||||
}
|
||||
|
||||
if s.EncryptionKey != nil {
|
||||
m.addKeyValue("k=", s.EncryptionKey.String())
|
||||
}
|
||||
|
||||
for _, a := range s.Attributes {
|
||||
m.addKeyValue("a=", a.String())
|
||||
}
|
||||
|
||||
for _, md := range s.MediaDescriptions {
|
||||
m.addKeyValue("m=", md.MediaName.String())
|
||||
|
||||
if md.MediaTitle != nil {
|
||||
m.addKeyValue("i=", md.MediaTitle.String())
|
||||
}
|
||||
|
||||
if md.ConnectionInformation != nil {
|
||||
m.addKeyValue("c=", md.ConnectionInformation.String())
|
||||
}
|
||||
|
||||
for _, b := range md.Bandwidth {
|
||||
m.addKeyValue("b=", b.String())
|
||||
}
|
||||
|
||||
if md.EncryptionKey != nil {
|
||||
m.addKeyValue("k=", md.EncryptionKey.String())
|
||||
}
|
||||
|
||||
for _, a := range md.Attributes {
|
||||
m.addKeyValue("a=", a.String())
|
||||
}
|
||||
}
|
||||
|
||||
return m.bytes(), nil
|
||||
}
|
||||
|
||||
// marshaller contains state during marshaling.
|
||||
type marshaller []byte
|
||||
|
||||
func (m *marshaller) addKeyValue(key, value string) {
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
*m = append(*m, key...)
|
||||
*m = append(*m, value...)
|
||||
*m = append(*m, "\r\n"...)
|
||||
}
|
||||
|
||||
func (m *marshaller) bytes() []byte {
|
||||
return *m
|
||||
}
|
80
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/media_description.go
generated
vendored
Normal file
80
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/media_description.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MediaDescription represents a media type.
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
type MediaDescription struct {
|
||||
// m=<media> <port>/<number of ports> <proto> <fmt> ...
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
MediaName MediaName
|
||||
|
||||
// i=<session description>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.4
|
||||
MediaTitle *Information
|
||||
|
||||
// c=<nettype> <addrtype> <connection-address>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.7
|
||||
ConnectionInformation *ConnectionInformation
|
||||
|
||||
// b=<bwtype>:<bandwidth>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.8
|
||||
Bandwidth []Bandwidth
|
||||
|
||||
// k=<method>
|
||||
// k=<method>:<encryption key>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.12
|
||||
EncryptionKey *EncryptionKey
|
||||
|
||||
// a=<attribute>
|
||||
// a=<attribute>:<value>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.13
|
||||
Attributes []Attribute
|
||||
}
|
||||
|
||||
// Attribute returns the value of an attribute and if it exists
|
||||
func (d *MediaDescription) Attribute(key string) (string, bool) {
|
||||
for _, a := range d.Attributes {
|
||||
if a.Key == key {
|
||||
return a.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// RangedPort supports special format for the media field "m=" port value. If
|
||||
// it may be necessary to specify multiple transport ports, the protocol allows
|
||||
// to write it as: <port>/<number of ports> where number of ports is a an
|
||||
// offsetting range.
|
||||
type RangedPort struct {
|
||||
Value int
|
||||
Range *int
|
||||
}
|
||||
|
||||
func (p *RangedPort) String() string {
|
||||
output := strconv.Itoa(p.Value)
|
||||
if p.Range != nil {
|
||||
output += "/" + strconv.Itoa(*p.Range)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// MediaName describes the "m=" field storage structure.
|
||||
type MediaName struct {
|
||||
Media string
|
||||
Port RangedPort
|
||||
Protos []string
|
||||
Formats []string
|
||||
}
|
||||
|
||||
func (m MediaName) String() string {
|
||||
return strings.Join([]string{
|
||||
m.Media,
|
||||
m.Port.String(),
|
||||
strings.Join(m.Protos, "/"),
|
||||
strings.Join(m.Formats, " "),
|
||||
}, " ")
|
||||
}
|
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/renovate.json
generated
vendored
Normal file
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/renovate.json
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"postUpdateOptions": [
|
||||
"gomodTidy"
|
||||
],
|
||||
"commitBody": "Generated by renovateBot",
|
||||
"packageRules": [
|
||||
{
|
||||
"packagePatterns": ["^golang.org/x/"],
|
||||
"schedule": ["on the first day of the month"]
|
||||
}
|
||||
]
|
||||
}
|
2
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/sdp.go
generated
vendored
Normal file
2
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/sdp.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Package sdp implements Session Description Protocol (SDP)
|
||||
package sdp
|
146
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/session_description.go
generated
vendored
Normal file
146
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/session_description.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SessionDescription is a a well-defined format for conveying sufficient
|
||||
// information to discover and participate in a multimedia session.
|
||||
type SessionDescription struct {
|
||||
// v=0
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.1
|
||||
Version Version
|
||||
|
||||
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.2
|
||||
Origin Origin
|
||||
|
||||
// s=<session name>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.3
|
||||
SessionName SessionName
|
||||
|
||||
// i=<session description>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.4
|
||||
SessionInformation *Information
|
||||
|
||||
// u=<uri>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.5
|
||||
URI *url.URL
|
||||
|
||||
// e=<email-address>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.6
|
||||
EmailAddress *EmailAddress
|
||||
|
||||
// p=<phone-number>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.6
|
||||
PhoneNumber *PhoneNumber
|
||||
|
||||
// c=<nettype> <addrtype> <connection-address>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.7
|
||||
ConnectionInformation *ConnectionInformation
|
||||
|
||||
// b=<bwtype>:<bandwidth>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.8
|
||||
Bandwidth []Bandwidth
|
||||
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.9
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.10
|
||||
TimeDescriptions []TimeDescription
|
||||
|
||||
// z=<adjustment time> <offset> <adjustment time> <offset> ...
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.11
|
||||
TimeZones []TimeZone
|
||||
|
||||
// k=<method>
|
||||
// k=<method>:<encryption key>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.12
|
||||
EncryptionKey *EncryptionKey
|
||||
|
||||
// a=<attribute>
|
||||
// a=<attribute>:<value>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.13
|
||||
Attributes []Attribute
|
||||
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
MediaDescriptions []*MediaDescription
|
||||
}
|
||||
|
||||
// Attribute returns the value of an attribute and if it exists
|
||||
func (s *SessionDescription) Attribute(key string) (string, bool) {
|
||||
for _, a := range s.Attributes {
|
||||
if a.Key == key {
|
||||
return a.Value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Version describes the value provided by the "v=" field which gives
|
||||
// the version of the Session Description Protocol.
|
||||
type Version int
|
||||
|
||||
func (v Version) String() string {
|
||||
return strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
// Origin defines the structure for the "o=" field which provides the
|
||||
// originator of the session plus a session identifier and version number.
|
||||
type Origin struct {
|
||||
Username string
|
||||
SessionID uint64
|
||||
SessionVersion uint64
|
||||
NetworkType string
|
||||
AddressType string
|
||||
UnicastAddress string
|
||||
}
|
||||
|
||||
func (o Origin) String() string {
|
||||
return fmt.Sprintf(
|
||||
"%v %d %d %v %v %v",
|
||||
o.Username,
|
||||
o.SessionID,
|
||||
o.SessionVersion,
|
||||
o.NetworkType,
|
||||
o.AddressType,
|
||||
o.UnicastAddress,
|
||||
)
|
||||
}
|
||||
|
||||
// SessionName describes a structured representations for the "s=" field
|
||||
// and is the textual session name.
|
||||
type SessionName string
|
||||
|
||||
func (s SessionName) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// EmailAddress describes a structured representations for the "e=" line
|
||||
// which specifies email contact information for the person responsible for
|
||||
// the conference.
|
||||
type EmailAddress string
|
||||
|
||||
func (e EmailAddress) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// PhoneNumber describes a structured representations for the "p=" line
|
||||
// specify phone contact information for the person responsible for the
|
||||
// conference.
|
||||
type PhoneNumber string
|
||||
|
||||
func (p PhoneNumber) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
// TimeZone defines the structured object for "z=" line which describes
|
||||
// repeated sessions scheduling.
|
||||
type TimeZone struct {
|
||||
AdjustmentTime uint64
|
||||
Offset int64
|
||||
}
|
||||
|
||||
func (z TimeZone) String() string {
|
||||
return strconv.FormatUint(z.AdjustmentTime, 10) + " " + strconv.FormatInt(z.Offset, 10)
|
||||
}
|
51
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/time_description.go
generated
vendored
Normal file
51
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/time_description.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TimeDescription describes "t=", "r=" fields of the session description
|
||||
// which are used to specify the start and stop times for a session as well as
|
||||
// repeat intervals and durations for the scheduled session.
|
||||
type TimeDescription struct {
|
||||
// t=<start-time> <stop-time>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.9
|
||||
Timing Timing
|
||||
|
||||
// r=<repeat interval> <active duration> <offsets from start-time>
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.10
|
||||
RepeatTimes []RepeatTime
|
||||
}
|
||||
|
||||
// Timing defines the "t=" field's structured representation for the start and
|
||||
// stop times.
|
||||
type Timing struct {
|
||||
StartTime uint64
|
||||
StopTime uint64
|
||||
}
|
||||
|
||||
func (t Timing) String() string {
|
||||
output := strconv.FormatUint(t.StartTime, 10)
|
||||
output += " " + strconv.FormatUint(t.StopTime, 10)
|
||||
return output
|
||||
}
|
||||
|
||||
// RepeatTime describes the "r=" fields of the session description which
|
||||
// represents the intervals and durations for repeated scheduled sessions.
|
||||
type RepeatTime struct {
|
||||
Interval int64
|
||||
Duration int64
|
||||
Offsets []int64
|
||||
}
|
||||
|
||||
func (r RepeatTime) String() string {
|
||||
fields := make([]string, 0)
|
||||
fields = append(fields, strconv.FormatInt(r.Interval, 10))
|
||||
fields = append(fields, strconv.FormatInt(r.Duration, 10))
|
||||
for _, value := range r.Offsets {
|
||||
fields = append(fields, strconv.FormatInt(value, 10))
|
||||
}
|
||||
|
||||
return strings.Join(fields, " ")
|
||||
}
|
907
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go
generated
vendored
Normal file
907
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/unmarshal.go
generated
vendored
Normal file
|
@ -0,0 +1,907 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
errSDPInvalidSyntax = errors.New("sdp: invalid syntax")
|
||||
errSDPInvalidNumericValue = errors.New("sdp: invalid numeric value")
|
||||
errSDPInvalidValue = errors.New("sdp: invalid value")
|
||||
errSDPInvalidPortValue = errors.New("sdp: invalid port value")
|
||||
)
|
||||
|
||||
// Unmarshal is the primary function that deserializes the session description
|
||||
// message and stores it inside of a structured SessionDescription object.
|
||||
//
|
||||
// The States Transition Table describes the computation flow between functions
|
||||
// (namely s1, s2, s3, ...) for a parsing procedure that complies with the
|
||||
// specifications laid out by the rfc4566#section-5 as well as by JavaScript
|
||||
// Session Establishment Protocol draft. Links:
|
||||
// https://tools.ietf.org/html/rfc4566#section-5
|
||||
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc4566#section-5
|
||||
// Session description
|
||||
// v= (protocol version)
|
||||
// o= (originator and session identifier)
|
||||
// s= (session name)
|
||||
// i=* (session information)
|
||||
// u=* (URI of description)
|
||||
// e=* (email address)
|
||||
// p=* (phone number)
|
||||
// c=* (connection information -- not required if included in
|
||||
// all media)
|
||||
// b=* (zero or more bandwidth information lines)
|
||||
// One or more time descriptions ("t=" and "r=" lines; see below)
|
||||
// z=* (time zone adjustments)
|
||||
// k=* (encryption key)
|
||||
// a=* (zero or more session attribute lines)
|
||||
// Zero or more media descriptions
|
||||
//
|
||||
// Time description
|
||||
// t= (time the session is active)
|
||||
// r=* (zero or more repeat times)
|
||||
//
|
||||
// Media description, if present
|
||||
// m= (media name and transport address)
|
||||
// i=* (media title)
|
||||
// c=* (connection information -- optional if included at
|
||||
// session level)
|
||||
// b=* (zero or more bandwidth information lines)
|
||||
// k=* (encryption key)
|
||||
// a=* (zero or more media attribute lines)
|
||||
//
|
||||
// In order to generate the following state table and draw subsequent
|
||||
// deterministic finite-state automota ("DFA") the following regex was used to
|
||||
// derive the DFA:
|
||||
// vosi?u?e?p?c?b*(tr*)+z?k?a*(mi?c?b*k?a*)*
|
||||
// possible place and state to exit:
|
||||
// ** * * * ** * * * *
|
||||
// 99 1 1 1 11 1 1 1 1
|
||||
// 3 1 1 26 5 5 4 4
|
||||
//
|
||||
// Please pay close attention to the `k`, and `a` parsing states. In the table
|
||||
// below in order to distinguish between the states belonging to the media
|
||||
// description as opposed to the session description, the states are marked
|
||||
// with an asterisk ("a*", "k*").
|
||||
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
|
||||
// | STATES | a* | a*,k* | a | a,k | b | b,c | e | i | m | o | p | r,t | s | t | u | v | z |
|
||||
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
|
||||
// | s1 | | | | | | | | | | | | | | | | 2 | |
|
||||
// | s2 | | | | | | | | | | 3 | | | | | | | |
|
||||
// | s3 | | | | | | | | | | | | | 4 | | | | |
|
||||
// | s4 | | | | | | 5 | 6 | 7 | | | 8 | | | 9 | 10 | | |
|
||||
// | s5 | | | | | 5 | | | | | | | | | 9 | | | |
|
||||
// | s6 | | | | | | 5 | | | | | 8 | | | 9 | | | |
|
||||
// | s7 | | | | | | 5 | 6 | | | | 8 | | | 9 | 10 | | |
|
||||
// | s8 | | | | | | 5 | | | | | | | | 9 | | | |
|
||||
// | s9 | | | | 11 | | | | | 12 | | | 9 | | | | | 13 |
|
||||
// | s10 | | | | | | 5 | 6 | | | | 8 | | | 9 | | | |
|
||||
// | s11 | | | 11 | | | | | | 12 | | | | | | | | |
|
||||
// | s12 | | 14 | | | | 15 | | 16 | 12 | | | | | | | | |
|
||||
// | s13 | | | | 11 | | | | | 12 | | | | | | | | |
|
||||
// | s14 | 14 | | | | | | | | 12 | | | | | | | | |
|
||||
// | s15 | | 14 | | | 15 | | | | 12 | | | | | | | | |
|
||||
// | s16 | | 14 | | | | 15 | | | 12 | | | | | | | | |
|
||||
// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
|
||||
func (s *SessionDescription) Unmarshal(value []byte) error {
|
||||
l := new(lexer)
|
||||
l.desc = s
|
||||
l.value = value
|
||||
for state := s1; state != nil; {
|
||||
var err error
|
||||
state, err = state(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func s1(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
if key == "v=" {
|
||||
return unmarshalProtocolVersion
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s2(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
if key == "o=" {
|
||||
return unmarshalOrigin
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s3(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
if key == "s=" {
|
||||
return unmarshalSessionName
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s4(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "i=":
|
||||
return unmarshalSessionInformation
|
||||
case "u=":
|
||||
return unmarshalURI
|
||||
case "e=":
|
||||
return unmarshalEmail
|
||||
case "p=":
|
||||
return unmarshalPhone
|
||||
case "c=":
|
||||
return unmarshalSessionConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s5(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s6(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "p=":
|
||||
return unmarshalPhone
|
||||
case "c=":
|
||||
return unmarshalSessionConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s7(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "u=":
|
||||
return unmarshalURI
|
||||
case "e=":
|
||||
return unmarshalEmail
|
||||
case "p=":
|
||||
return unmarshalPhone
|
||||
case "c=":
|
||||
return unmarshalSessionConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s8(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "c=":
|
||||
return unmarshalSessionConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s9(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "z=":
|
||||
return unmarshalTimeZones
|
||||
case "k=":
|
||||
return unmarshalSessionEncryptionKey
|
||||
case "a=":
|
||||
return unmarshalSessionAttribute
|
||||
case "r=":
|
||||
return unmarshalRepeatTimes
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s10(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "e=":
|
||||
return unmarshalEmail
|
||||
case "p=":
|
||||
return unmarshalPhone
|
||||
case "c=":
|
||||
return unmarshalSessionConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalSessionBandwidth
|
||||
case "t=":
|
||||
return unmarshalTiming
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s11(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalSessionAttribute
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s12(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalMediaAttribute
|
||||
case "k=":
|
||||
return unmarshalMediaEncryptionKey
|
||||
case "b=":
|
||||
return unmarshalMediaBandwidth
|
||||
case "c=":
|
||||
return unmarshalMediaConnectionInformation
|
||||
case "i=":
|
||||
return unmarshalMediaTitle
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s13(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalSessionAttribute
|
||||
case "k=":
|
||||
return unmarshalSessionEncryptionKey
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s14(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalMediaAttribute
|
||||
case "k=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaEncryptionKey
|
||||
case "b=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaBandwidth
|
||||
case "c=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaConnectionInformation
|
||||
case "i=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaTitle
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s15(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalMediaAttribute
|
||||
case "k=":
|
||||
return unmarshalMediaEncryptionKey
|
||||
case "b=":
|
||||
return unmarshalMediaBandwidth
|
||||
case "c=":
|
||||
return unmarshalMediaConnectionInformation
|
||||
case "i=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaTitle
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func s16(l *lexer) (stateFn, error) {
|
||||
return l.handleType(func(key string) stateFn {
|
||||
switch key {
|
||||
case "a=":
|
||||
return unmarshalMediaAttribute
|
||||
case "k=":
|
||||
return unmarshalMediaEncryptionKey
|
||||
case "c=":
|
||||
return unmarshalMediaConnectionInformation
|
||||
case "b=":
|
||||
return unmarshalMediaBandwidth
|
||||
case "i=":
|
||||
// Non-spec ordering
|
||||
return unmarshalMediaTitle
|
||||
case "m=":
|
||||
return unmarshalMediaDescription
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func unmarshalProtocolVersion(l *lexer) (stateFn, error) {
|
||||
version, err := l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// As off the latest draft of the rfc this value is required to be 0.
|
||||
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-5.8.1
|
||||
if version != 0 {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, version)
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s2, nil
|
||||
}
|
||||
|
||||
func unmarshalOrigin(l *lexer) (stateFn, error) {
|
||||
var err error
|
||||
|
||||
l.desc.Origin.Username, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.Origin.SessionID, err = l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.Origin.SessionVersion, err = l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.Origin.NetworkType, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-8.2.6
|
||||
if !anyOf(l.desc.Origin.NetworkType, "IN") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, l.desc.Origin.NetworkType)
|
||||
}
|
||||
|
||||
l.desc.Origin.AddressType, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-8.2.7
|
||||
if !anyOf(l.desc.Origin.AddressType, "IP4", "IP6") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, l.desc.Origin.AddressType)
|
||||
}
|
||||
|
||||
l.desc.Origin.UnicastAddress, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s3, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionName(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.SessionName = SessionName(value)
|
||||
return s4, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionInformation(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sessionInformation := Information(value)
|
||||
l.desc.SessionInformation = &sessionInformation
|
||||
return s7, nil
|
||||
}
|
||||
|
||||
func unmarshalURI(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.URI, err = url.Parse(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s10, nil
|
||||
}
|
||||
|
||||
func unmarshalEmail(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
emailAddress := EmailAddress(value)
|
||||
l.desc.EmailAddress = &emailAddress
|
||||
return s6, nil
|
||||
}
|
||||
|
||||
func unmarshalPhone(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
phoneNumber := PhoneNumber(value)
|
||||
l.desc.PhoneNumber = &phoneNumber
|
||||
return s8, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionConnectionInformation(l *lexer) (stateFn, error) {
|
||||
var err error
|
||||
l.desc.ConnectionInformation, err = l.unmarshalConnectionInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s5, nil
|
||||
}
|
||||
|
||||
func (l *lexer) unmarshalConnectionInformation() (*ConnectionInformation, error) {
|
||||
var err error
|
||||
var c ConnectionInformation
|
||||
|
||||
c.NetworkType, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-8.2.6
|
||||
if !anyOf(c.NetworkType, "IN") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, c.NetworkType)
|
||||
}
|
||||
|
||||
c.AddressType, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-8.2.7
|
||||
if !anyOf(c.AddressType, "IP4", "IP6") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, c.AddressType)
|
||||
}
|
||||
|
||||
address, err := l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if address != "" {
|
||||
c.Address = new(Address)
|
||||
c.Address.Address = address
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionBandwidth(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bandwidth, err := unmarshalBandwidth(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, value)
|
||||
}
|
||||
l.desc.Bandwidth = append(l.desc.Bandwidth, *bandwidth)
|
||||
|
||||
return s5, nil
|
||||
}
|
||||
|
||||
func unmarshalBandwidth(value string) (*Bandwidth, error) {
|
||||
parts := strings.Split(value, ":")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, parts)
|
||||
}
|
||||
|
||||
experimental := strings.HasPrefix(parts[0], "X-")
|
||||
if experimental {
|
||||
parts[0] = strings.TrimPrefix(parts[0], "X-")
|
||||
} else if !anyOf(parts[0], "CT", "AS") {
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.8
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, parts[0])
|
||||
}
|
||||
|
||||
bandwidth, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, parts[1])
|
||||
}
|
||||
|
||||
return &Bandwidth{
|
||||
Experimental: experimental,
|
||||
Type: parts[0],
|
||||
Bandwidth: bandwidth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func unmarshalTiming(l *lexer) (stateFn, error) {
|
||||
var err error
|
||||
var td TimeDescription
|
||||
|
||||
td.Timing.StartTime, err = l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
td.Timing.StopTime, err = l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.TimeDescriptions = append(l.desc.TimeDescriptions, td)
|
||||
return s9, nil
|
||||
}
|
||||
|
||||
func unmarshalRepeatTimes(l *lexer) (stateFn, error) {
|
||||
var err error
|
||||
var newRepeatTime RepeatTime
|
||||
|
||||
latestTimeDesc := &l.desc.TimeDescriptions[len(l.desc.TimeDescriptions)-1]
|
||||
|
||||
field, err := l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newRepeatTime.Interval, err = parseTimeUnits(field)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
|
||||
}
|
||||
|
||||
field, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newRepeatTime.Duration, err = parseTimeUnits(field)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
|
||||
}
|
||||
|
||||
for {
|
||||
field, err := l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == "" {
|
||||
break
|
||||
}
|
||||
offset, err := parseTimeUnits(field)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
|
||||
}
|
||||
newRepeatTime.Offsets = append(newRepeatTime.Offsets, offset)
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestTimeDesc.RepeatTimes = append(latestTimeDesc.RepeatTimes, newRepeatTime)
|
||||
return s9, nil
|
||||
}
|
||||
|
||||
func unmarshalTimeZones(l *lexer) (stateFn, error) {
|
||||
// These fields are transimitted in pairs
|
||||
// z=<adjustment time> <offset> <adjustment time> <offset> ....
|
||||
// so we are making sure that there are actually multiple of 2 total.
|
||||
for {
|
||||
var err error
|
||||
var timeZone TimeZone
|
||||
|
||||
timeZone.AdjustmentTime, err = l.readUint64Field()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
offset, err := l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if offset == "" {
|
||||
break
|
||||
}
|
||||
|
||||
timeZone.Offset, err = parseTimeUnits(offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.TimeZones = append(l.desc.TimeZones, timeZone)
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s13, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionEncryptionKey(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptionKey := EncryptionKey(value)
|
||||
l.desc.EncryptionKey = &encryptionKey
|
||||
return s11, nil
|
||||
}
|
||||
|
||||
func unmarshalSessionAttribute(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := strings.IndexRune(value, ':')
|
||||
var a Attribute
|
||||
if i > 0 {
|
||||
a = NewAttribute(value[:i], value[i+1:])
|
||||
} else {
|
||||
a = NewPropertyAttribute(value)
|
||||
}
|
||||
|
||||
l.desc.Attributes = append(l.desc.Attributes, a)
|
||||
return s11, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaDescription(l *lexer) (stateFn, error) {
|
||||
var newMediaDesc MediaDescription
|
||||
|
||||
// <media>
|
||||
field, err := l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
if !anyOf(field, "audio", "video", "text", "application", "message") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
|
||||
}
|
||||
newMediaDesc.MediaName.Media = field
|
||||
|
||||
// <port>
|
||||
field, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parts := strings.Split(field, "/")
|
||||
newMediaDesc.MediaName.Port.Value, err = parsePort(parts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, parts[0])
|
||||
}
|
||||
|
||||
if len(parts) > 1 {
|
||||
var portRange int
|
||||
portRange, err = strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, parts)
|
||||
}
|
||||
newMediaDesc.MediaName.Port.Range = &portRange
|
||||
}
|
||||
|
||||
// <proto>
|
||||
field, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set according to currently registered with IANA
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.14
|
||||
for _, proto := range strings.Split(field, "/") {
|
||||
if !anyOf(proto, "UDP", "RTP", "AVP", "SAVP", "SAVPF", "TLS", "DTLS", "SCTP", "AVPF") {
|
||||
return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, field)
|
||||
}
|
||||
newMediaDesc.MediaName.Protos = append(newMediaDesc.MediaName.Protos, proto)
|
||||
}
|
||||
|
||||
// <fmt>...
|
||||
for {
|
||||
field, err = l.readField()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field == "" {
|
||||
break
|
||||
}
|
||||
newMediaDesc.MediaName.Formats = append(newMediaDesc.MediaName.Formats, field)
|
||||
}
|
||||
|
||||
if err := l.nextLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.desc.MediaDescriptions = append(l.desc.MediaDescriptions, &newMediaDesc)
|
||||
return s12, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaTitle(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
|
||||
mediaTitle := Information(value)
|
||||
latestMediaDesc.MediaTitle = &mediaTitle
|
||||
return s16, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaConnectionInformation(l *lexer) (stateFn, error) {
|
||||
var err error
|
||||
latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
|
||||
latestMediaDesc.ConnectionInformation, err = l.unmarshalConnectionInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s15, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaBandwidth(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
|
||||
bandwidth, err := unmarshalBandwidth(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidSyntax, value)
|
||||
}
|
||||
latestMediaDesc.Bandwidth = append(latestMediaDesc.Bandwidth, *bandwidth)
|
||||
return s15, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaEncryptionKey(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
|
||||
encryptionKey := EncryptionKey(value)
|
||||
latestMediaDesc.EncryptionKey = &encryptionKey
|
||||
return s14, nil
|
||||
}
|
||||
|
||||
func unmarshalMediaAttribute(l *lexer) (stateFn, error) {
|
||||
value, err := l.readLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := strings.IndexRune(value, ':')
|
||||
var a Attribute
|
||||
if i > 0 {
|
||||
a = NewAttribute(value[:i], value[i+1:])
|
||||
} else {
|
||||
a = NewPropertyAttribute(value)
|
||||
}
|
||||
|
||||
latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
|
||||
latestMediaDesc.Attributes = append(latestMediaDesc.Attributes, a)
|
||||
return s14, nil
|
||||
}
|
||||
|
||||
func parseTimeUnits(value string) (num int64, err error) {
|
||||
k := timeShorthand(value[len(value)-1])
|
||||
if k > 0 {
|
||||
num, err = strconv.ParseInt(value[:len(value)-1], 10, 64)
|
||||
} else {
|
||||
k = 1
|
||||
num, err = strconv.ParseInt(value, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%w `%v`", errSDPInvalidValue, value)
|
||||
}
|
||||
return num * k, nil
|
||||
}
|
||||
|
||||
func timeShorthand(b byte) int64 {
|
||||
// Some time offsets in the protocol can be provided with a shorthand
|
||||
// notation. This code ensures to convert it to NTP timestamp format.
|
||||
switch b {
|
||||
case 'd': // days
|
||||
return 86400
|
||||
case 'h': // hours
|
||||
return 3600
|
||||
case 'm': // minutes
|
||||
return 60
|
||||
case 's': // seconds (allowed for completeness)
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func parsePort(value string) (int, error) {
|
||||
port, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, port)
|
||||
}
|
||||
|
||||
if port < 0 || port > 65536 {
|
||||
return 0, fmt.Errorf("%w -- out of range `%v`", errSDPInvalidPortValue, port)
|
||||
}
|
||||
|
||||
return port, nil
|
||||
}
|
318
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go
generated
vendored
Normal file
318
trunk/3rdparty/srs-bench/vendor/github.com/pion/sdp/v3/util.go
generated
vendored
Normal file
|
@ -0,0 +1,318 @@
|
|||
package sdp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/randutil"
|
||||
)
|
||||
|
||||
const (
|
||||
attributeKey = "a="
|
||||
)
|
||||
|
||||
var (
|
||||
errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
|
||||
errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
|
||||
errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
|
||||
errPayloadTypeNotFound = errors.New("payload type not found")
|
||||
errCodecNotFound = errors.New("codec not found")
|
||||
errSyntaxError = errors.New("SyntaxError")
|
||||
)
|
||||
|
||||
// ConnectionRole indicates which of the end points should initiate the connection establishment
|
||||
type ConnectionRole int
|
||||
|
||||
const (
|
||||
// ConnectionRoleActive indicates the endpoint will initiate an outgoing connection.
|
||||
ConnectionRoleActive ConnectionRole = iota + 1
|
||||
|
||||
// ConnectionRolePassive indicates the endpoint will accept an incoming connection.
|
||||
ConnectionRolePassive
|
||||
|
||||
// ConnectionRoleActpass indicates the endpoint is willing to accept an incoming connection or to initiate an outgoing connection.
|
||||
ConnectionRoleActpass
|
||||
|
||||
// ConnectionRoleHoldconn indicates the endpoint does not want the connection to be established for the time being.
|
||||
ConnectionRoleHoldconn
|
||||
)
|
||||
|
||||
func (t ConnectionRole) String() string {
|
||||
switch t {
|
||||
case ConnectionRoleActive:
|
||||
return "active"
|
||||
case ConnectionRolePassive:
|
||||
return "passive"
|
||||
case ConnectionRoleActpass:
|
||||
return "actpass"
|
||||
case ConnectionRoleHoldconn:
|
||||
return "holdconn"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func newSessionID() (uint64, error) {
|
||||
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26#section-5.2.1
|
||||
// Session ID is recommended to be constructed by generating a 64-bit
|
||||
// quantity with the highest bit set to zero and the remaining 63-bits
|
||||
// being cryptographically random.
|
||||
id, err := randutil.CryptoUint64()
|
||||
return id & (^(uint64(1) << 63)), err
|
||||
}
|
||||
|
||||
// Codec represents a codec
|
||||
type Codec struct {
|
||||
PayloadType uint8
|
||||
Name string
|
||||
ClockRate uint32
|
||||
EncodingParameters string
|
||||
Fmtp string
|
||||
RTCPFeedback []string
|
||||
}
|
||||
|
||||
const (
|
||||
unknown = iota
|
||||
)
|
||||
|
||||
func (c Codec) String() string {
|
||||
return fmt.Sprintf("%d %s/%d/%s (%s) [%s]", c.PayloadType, c.Name, c.ClockRate, c.EncodingParameters, c.Fmtp, strings.Join(c.RTCPFeedback, ", "))
|
||||
}
|
||||
|
||||
func parseRtpmap(rtpmap string) (Codec, error) {
|
||||
var codec Codec
|
||||
parsingFailed := errExtractCodecRtpmap
|
||||
|
||||
// a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
|
||||
split := strings.Split(rtpmap, " ")
|
||||
if len(split) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
ptSplit := strings.Split(split[0], ":")
|
||||
if len(ptSplit) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
ptInt, err := strconv.Atoi(ptSplit[1])
|
||||
if err != nil {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
codec.PayloadType = uint8(ptInt)
|
||||
|
||||
split = strings.Split(split[1], "/")
|
||||
codec.Name = split[0]
|
||||
parts := len(split)
|
||||
if parts > 1 {
|
||||
rate, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
codec.ClockRate = uint32(rate)
|
||||
}
|
||||
if parts > 2 {
|
||||
codec.EncodingParameters = split[2]
|
||||
}
|
||||
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
func parseFmtp(fmtp string) (Codec, error) {
|
||||
var codec Codec
|
||||
parsingFailed := errExtractCodecFmtp
|
||||
|
||||
// a=fmtp:<format> <format specific parameters>
|
||||
split := strings.Split(fmtp, " ")
|
||||
if len(split) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
formatParams := split[1]
|
||||
|
||||
split = strings.Split(split[0], ":")
|
||||
if len(split) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
ptInt, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
codec.PayloadType = uint8(ptInt)
|
||||
codec.Fmtp = formatParams
|
||||
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
func parseRtcpFb(rtcpFb string) (Codec, error) {
|
||||
var codec Codec
|
||||
parsingFailed := errExtractCodecRtcpFb
|
||||
|
||||
// a=ftcp-fb:<payload type> <RTCP feedback type> [<RTCP feedback parameter>]
|
||||
split := strings.SplitN(rtcpFb, " ", 2)
|
||||
if len(split) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
ptSplit := strings.Split(split[0], ":")
|
||||
if len(ptSplit) != 2 {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
ptInt, err := strconv.Atoi(ptSplit[1])
|
||||
if err != nil {
|
||||
return codec, parsingFailed
|
||||
}
|
||||
|
||||
codec.PayloadType = uint8(ptInt)
|
||||
codec.RTCPFeedback = append(codec.RTCPFeedback, split[1])
|
||||
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
func mergeCodecs(codec Codec, codecs map[uint8]Codec) {
|
||||
savedCodec := codecs[codec.PayloadType]
|
||||
|
||||
if savedCodec.PayloadType == 0 {
|
||||
savedCodec.PayloadType = codec.PayloadType
|
||||
}
|
||||
if savedCodec.Name == "" {
|
||||
savedCodec.Name = codec.Name
|
||||
}
|
||||
if savedCodec.ClockRate == 0 {
|
||||
savedCodec.ClockRate = codec.ClockRate
|
||||
}
|
||||
if savedCodec.EncodingParameters == "" {
|
||||
savedCodec.EncodingParameters = codec.EncodingParameters
|
||||
}
|
||||
if savedCodec.Fmtp == "" {
|
||||
savedCodec.Fmtp = codec.Fmtp
|
||||
}
|
||||
savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
|
||||
|
||||
codecs[savedCodec.PayloadType] = savedCodec
|
||||
}
|
||||
|
||||
func (s *SessionDescription) buildCodecMap() map[uint8]Codec {
|
||||
codecs := make(map[uint8]Codec)
|
||||
|
||||
for _, m := range s.MediaDescriptions {
|
||||
for _, a := range m.Attributes {
|
||||
attr := a.String()
|
||||
switch {
|
||||
case strings.HasPrefix(attr, "rtpmap:"):
|
||||
codec, err := parseRtpmap(attr)
|
||||
if err == nil {
|
||||
mergeCodecs(codec, codecs)
|
||||
}
|
||||
case strings.HasPrefix(attr, "fmtp:"):
|
||||
codec, err := parseFmtp(attr)
|
||||
if err == nil {
|
||||
mergeCodecs(codec, codecs)
|
||||
}
|
||||
case strings.HasPrefix(attr, "rtcp-fb:"):
|
||||
codec, err := parseRtcpFb(attr)
|
||||
if err == nil {
|
||||
mergeCodecs(codec, codecs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return codecs
|
||||
}
|
||||
|
||||
func equivalentFmtp(want, got string) bool {
|
||||
wantSplit := strings.Split(want, ";")
|
||||
gotSplit := strings.Split(got, ";")
|
||||
|
||||
if len(wantSplit) != len(gotSplit) {
|
||||
return false
|
||||
}
|
||||
|
||||
sort.Strings(wantSplit)
|
||||
sort.Strings(gotSplit)
|
||||
|
||||
for i, wantPart := range wantSplit {
|
||||
wantPart = strings.TrimSpace(wantPart)
|
||||
gotPart := strings.TrimSpace(gotSplit[i])
|
||||
if gotPart != wantPart {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func codecsMatch(wanted, got Codec) bool {
|
||||
if wanted.Name != "" && !strings.EqualFold(wanted.Name, got.Name) {
|
||||
return false
|
||||
}
|
||||
if wanted.ClockRate != 0 && wanted.ClockRate != got.ClockRate {
|
||||
return false
|
||||
}
|
||||
if wanted.EncodingParameters != "" && wanted.EncodingParameters != got.EncodingParameters {
|
||||
return false
|
||||
}
|
||||
if wanted.Fmtp != "" && !equivalentFmtp(wanted.Fmtp, got.Fmtp) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec
|
||||
func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) {
|
||||
codecs := s.buildCodecMap()
|
||||
|
||||
codec, ok := codecs[payloadType]
|
||||
if ok {
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
return codec, errPayloadTypeNotFound
|
||||
}
|
||||
|
||||
// GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec
|
||||
// as closely as possible and returns its payload type
|
||||
func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) {
|
||||
codecs := s.buildCodecMap()
|
||||
|
||||
for payloadType, codec := range codecs {
|
||||
if codecsMatch(wanted, codec) {
|
||||
return payloadType, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errCodecNotFound
|
||||
}
|
||||
|
||||
type stateFn func(*lexer) (stateFn, error)
|
||||
|
||||
type lexer struct {
|
||||
desc *SessionDescription
|
||||
baseLexer
|
||||
}
|
||||
|
||||
type keyToState func(key string) stateFn
|
||||
|
||||
func (l *lexer) handleType(fn keyToState) (stateFn, error) {
|
||||
key, err := l.readType()
|
||||
if err == io.EOF && key == "" {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res := fn(key); res != nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return nil, l.syntaxError()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue