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
3
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.codacy.yaml
generated
vendored
Normal file
3
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.codacy.yaml
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
exclude_paths:
|
||||
- examples/examples.json
|
3
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.eslintrc.json
generated
vendored
Normal file
3
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.eslintrc.json
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": ["standard"]
|
||||
}
|
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/.gitignore
generated
vendored
Normal file
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/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/webrtc/v3/.golangci.yml
generated
vendored
Normal file
89
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/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
|
43
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/DESIGN.md
generated
vendored
Normal file
43
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/DESIGN.md
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<h1 align="center">
|
||||
Design
|
||||
</h1>
|
||||
WebRTC is a powerful, but complicated technology you can build amazing things with, it comes with a steep learning curve though.
|
||||
Using WebRTC in the browser is easy, but outside the browser is more of a challenge. There are multiple libraries, and they all have
|
||||
varying levels of quality. Most are also difficult to build, and depend on libraries that aren't available in repos or portable.
|
||||
|
||||
Pion WebRTC aims to solve all that! Built in native Go you should be able to send and receive media and text from anywhere with minimal headache.
|
||||
These are the design principals that drive Pion WebRTC and hopefully convince you it is worth a try.
|
||||
|
||||
### Portable
|
||||
Pion WebRTC is written in Go and extremely portable. Anywhere Golang runs, Pion WebRTC should work as well! Instead of dealing with complicated
|
||||
cross-compiling of multiple libraries, you now can run anywhere with one `go build`
|
||||
|
||||
### Flexible
|
||||
When possible we leave all decisions to the user. When choice is possible (like what logging library is used) we defer to the developer.
|
||||
|
||||
### Simple API
|
||||
If you know how to use WebRTC in your browser, you know how to use Pion WebRTC.
|
||||
We try our best just to duplicate the Javascript API, so your code can look the same everywhere.
|
||||
|
||||
If this is your first time using WebRTC, don't worry! We have multiple [examples](https://github.com/pion/webrtc/tree/master/examples) and [GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v3)
|
||||
|
||||
### Bring your own media
|
||||
Pion WebRTC doesn't make any assumptions about where your audio, video or text come from. You can use FFmpeg, GStreamer, MLT or just serve a video file.
|
||||
This library only serves to transport, not create media.
|
||||
|
||||
### Safe
|
||||
Golang provides a great foundation to build safe network services.
|
||||
Especially when running a networked service that is highly concurrent bugs can be devastating.
|
||||
|
||||
### Readable
|
||||
If code comes from an RFC we try to make sure everything is commented with a link to the spec.
|
||||
This makes learning and debugging easier, this WebRTC library was written to also serve as a guide for others.
|
||||
|
||||
### Tested
|
||||
Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
|
||||
|
||||
### Shared libraries
|
||||
Every Pion project is built using shared libraries, allowing others to review and reuse our libraries.
|
||||
|
||||
### Community
|
||||
The most important part of Pion is the community. This projects only exist because of individual contributions. We aim to be radically open and do everything we can to support those that make Pion possible.
|
21
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/LICENSE
generated
vendored
Normal file
21
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/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.
|
264
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/README.md
generated
vendored
Normal file
264
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/README.md
generated
vendored
Normal file
|
@ -0,0 +1,264 @@
|
|||
<h1 align="center">
|
||||
<a href="https://pion.ly"><img src="./.github/pion-gopher-webrtc.png" alt="Pion WebRTC" height="250px"></a>
|
||||
<br>
|
||||
Pion WebRTC
|
||||
<br>
|
||||
</h1>
|
||||
<h4 align="center">A pure Go implementation of the WebRTC API</h4>
|
||||
<p align="center">
|
||||
<a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-webrtc-gray.svg?longCache=true&colorB=brightgreen" alt="Pion webrtc"></a>
|
||||
<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/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>
|
||||
<a href="https://twitter.com/_pion?ref_src=twsrc%5Etfw"><img src="https://img.shields.io/twitter/url.svg?label=Follow%20%40_pion&style=social&url=https%3A%2F%2Ftwitter.com%2F_pion" alt="Twitter Widget"></a>
|
||||
<a href="https://github.com/pion/awesome-pion" alt="Awesome Pion"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"></a>
|
||||
<br>
|
||||
<a href="https://travis-ci.org/pion/webrtc"><img src="https://travis-ci.org/pion/webrtc.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://pkg.go.dev/github.com/pion/webrtc/v3"><img src="https://pkg.go.dev/badge/github.com/pion/webrtc/v3" alt="PkgGoDev"></a>
|
||||
<a href="https://codecov.io/gh/pion/webrtc"><img src="https://codecov.io/gh/pion/webrtc/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/pion/webrtc"><img src="https://goreportcard.com/badge/github.com/pion/webrtc" alt="Go Report Card"></a>
|
||||
<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
### New Release
|
||||
|
||||
Pion WebRTC v3.0.0 has been released! See the [release notes](https://github.com/pion/webrtc/wiki/Release-WebRTC@v3.0.0) to learn about new features and breaking changes.
|
||||
|
||||
If you aren't able to upgrade yet check the [tags](https://github.com/pion/webrtc/tags) for the latest `v2` release.
|
||||
|
||||
We would love your feedback! Please create GitHub issues or join [the Slack channel](https://pion.ly/slack) to follow development and speak with the maintainers.
|
||||
|
||||
----
|
||||
|
||||
### Usage
|
||||
[Go Modules](https://blog.golang.org/using-go-modules) are mandatory for using Pion WebRTC. So make sure you set `export GO111MODULE=on`, and explicitly specify `/v2` or `/v3` when importing.
|
||||
|
||||
|
||||
**[example applications](examples/README.md)** contains code samples of common things people build with Pion WebRTC.
|
||||
|
||||
**[example-webrtc-applications](https://github.com/pion/example-webrtc-applications)** contains more full featured examples that use 3rd party libraries.
|
||||
|
||||
**[awesome-pion](https://github.com/pion/awesome-pion)** contains projects that have used Pion, and serve as real world examples of usage.
|
||||
|
||||
**[GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v3)** is an auto generated API reference. All our Public APIs are commented.
|
||||
|
||||
**[FAQ](https://github.com/pion/webrtc/wiki/FAQ)** has answers to common questions. If you have a question not covered please ask in [Slack](https://pion.ly/slack) we are always looking to expand it.
|
||||
|
||||
Now go build something awesome! Here are some **ideas** to get your creative juices flowing:
|
||||
* Send a video file to multiple browser in real time for perfectly synchronized movie watching.
|
||||
* Send a webcam on an embedded device to your browser with no additional server required!
|
||||
* Securely send data between two servers, without using pub/sub.
|
||||
* Record your webcam and do special effects server side.
|
||||
* Build a conferencing application that processes audio/video and make decisions off of it.
|
||||
* Remotely control a robots and stream its cameras in realtime.
|
||||
|
||||
### Want to learn more about WebRTC?
|
||||
Check out [WebRTC for the Curious](https://webrtcforthecurious.com). A book about WebRTC in depth, not just about the APIs.
|
||||
Learn the full details of ICE, SCTP, DTLS, SRTP, and how they work together to make up the WebRTC stack.
|
||||
|
||||
This is also a great resource if you are trying to debug. Learn the tools of the trade and how to approach WebRTC issues.
|
||||
|
||||
This book is vendor agnostic and will not have any Pion specific information.
|
||||
|
||||
### Features
|
||||
#### PeerConnection API
|
||||
* Go implementation of [webrtc-pc](https://w3c.github.io/webrtc-pc/) and [webrtc-stats](https://www.w3.org/TR/webrtc-stats/)
|
||||
* DataChannels
|
||||
* Send/Receive audio and video
|
||||
* Renegotiation
|
||||
* Plan-B and Unified Plan
|
||||
* [SettingEngine](https://pkg.go.dev/github.com/pion/webrtc/v3#SettingEngine) for Pion specific extensions
|
||||
|
||||
|
||||
#### Connectivity
|
||||
* Full ICE Agent
|
||||
* ICE Restart
|
||||
* Trickle ICE
|
||||
* STUN
|
||||
* TURN (UDP, TCP, DTLS and TLS)
|
||||
* mDNS candidates
|
||||
|
||||
#### DataChannels
|
||||
* Ordered/Unordered
|
||||
* Lossy/Lossless
|
||||
|
||||
#### Media
|
||||
* API with direct RTP/RTCP access
|
||||
* Opus, PCM, H264, VP8 and VP9 packetizer
|
||||
* API also allows developer to pass their own packetizer
|
||||
* IVF, Ogg, H264 and Matroska provided for easy sending and saving
|
||||
* [getUserMedia](https://github.com/pion/mediadevices) implementation (Requires Cgo)
|
||||
* Easy integration with x264, libvpx, GStreamer and ffmpeg.
|
||||
* [Simulcast](https://github.com/pion/webrtc/tree/master/examples/simulcast)
|
||||
* [SVC](https://github.com/pion/rtp/blob/master/codecs/vp9_packet.go#L138)
|
||||
* [NACK](https://github.com/pion/interceptor/pull/4)
|
||||
* Full loss recovery and congestion control is not complete, see [pion/interceptor](https://github.com/pion/interceptor) for progress
|
||||
* See [ion](https://github.com/pion/ion-sfu/tree/master/pkg/buffer) for how an implementor can do it today
|
||||
|
||||
#### Security
|
||||
* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 and TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA for DTLS v1.2
|
||||
* SRTP_AEAD_AES_256_GCM and SRTP_AES128_CM_HMAC_SHA1_80 for SRTP
|
||||
* Hardware acceleration available for GCM suites
|
||||
|
||||
#### Pure Go
|
||||
* No Cgo usage
|
||||
* Wide platform support
|
||||
* Windows, macOS, Linux, FreeBSD
|
||||
* iOS, Android
|
||||
* [WASM](https://github.com/pion/webrtc/wiki/WebAssembly-Development-and-Testing) see [examples](examples/README.md#webassembly)
|
||||
* 386, amd64, arm, mips, ppc64
|
||||
* Easy to build *Numbers generated on Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz*
|
||||
* **Time to build examples/play-from-disk** - 0.66s user 0.20s system 306% cpu 0.279 total
|
||||
* **Time to run entire test suite** - 25.60s user 9.40s system 45% cpu 1:16.69 total
|
||||
* Tools to measure performance [provided](https://github.com/pion/rtsp-bench)
|
||||
|
||||
|
||||
### Roadmap
|
||||
The library is in active development, please refer to the [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
|
||||
We also maintain a list of [Big Ideas](https://github.com/pion/webrtc/wiki/Big-Ideas) these are things we want to build but don't have a clear plan or the resources yet.
|
||||
If you are looking to get involved this is a great place to get started! We would also love to hear your ideas! Even if you can't implement it yourself, it could inspire others.
|
||||
|
||||
### 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:
|
||||
|
||||
* [John Bradley](https://github.com/kc5nra) - *Original Author*
|
||||
* [Michael Melvin Santry](https://github.com/santrym) - *Mascot*
|
||||
* [Raphael Randschau](https://github.com/nicolai86) - *STUN*
|
||||
* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
|
||||
* [Michiel De Backker](https://github.com/backkem) - *SDP, Public API, Project Management*
|
||||
* [Brendan Rius](https://github.com/brendanrius) - *Cleanup*
|
||||
* [Konstantin Itskov](https://github.com/trivigy) - *SDP Parsing*
|
||||
* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
|
||||
* [Ronan J](https://github.com/ronanj) - *Fix STCP PPID*
|
||||
* [wattanakorn495](https://github.com/wattanakorn495)
|
||||
* [Max Hawkins](https://github.com/maxhawkins) - *RTCP*
|
||||
* [Justin Okamoto](https://github.com/justinokamoto) - *Fix Docs*
|
||||
* [leeoxiang](https://github.com/notedit) - *Implement Janus examples*
|
||||
* [Denis](https://github.com/Hixon10) - *Adding docker-compose to pion-to-pion example*
|
||||
* [earle](https://github.com/aguilEA) - *Generate DTLS fingerprint in Go*
|
||||
* [Jake B](https://github.com/silbinarywolf) - *Fix Windows installation instructions*
|
||||
* [Michael MacDonald](https://github.com/mjmac) - *Plan B compatibility, Remote TURN/Trickle-ICE, Logging framework*
|
||||
* [Oleg Kovalov](https://github.com/cristaloleg) *Use wildcards instead of hardcoding travis-ci config*
|
||||
* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
|
||||
* [Tobias Fridén](https://github.com/tobiasfriden) *SRTP authentication verification*
|
||||
* [Yutaka Takeda](https://github.com/enobufs) *Fix ICE connection timeout*
|
||||
* [Hugo Arregui](https://github.com/hugoArregui) *Fix connection timeout*
|
||||
* [Rob Deutsch](https://github.com/rob-deutsch) *RTPReceiver graceful shutdown*
|
||||
* [Jin Lei](https://github.com/jinleileiking) - *SFU example use http*
|
||||
* [Will Watson](https://github.com/wwatson) - *Enable gocritic*
|
||||
* [Luke Curley](https://github.com/kixelated)
|
||||
* [Antoine Baché](https://github.com/Antonito) - *OGG Opus export*
|
||||
* [frank](https://github.com/feixiao) - *Building examples on OSX*
|
||||
* [mxmCherry](https://github.com/mxmCherry)
|
||||
* [Alex Browne](https://github.com/albrow) - *JavaScript/Wasm bindings*
|
||||
* [adwpc](https://github.com/adwpc) - *SFU example with websocket*
|
||||
* [imalic3](https://github.com/imalic3) - *SFU websocket example with datachannel broadcast*
|
||||
* [Žiga Željko](https://github.com/zigazeljko)
|
||||
* [Simonacca Fotokite](https://github.com/simonacca-fotokite)
|
||||
* [Marouane](https://github.com/nindolabs) *Fix Offer bundle generation*
|
||||
* [Christopher Fry](https://github.com/christopherfry)
|
||||
* [Adam Kiss](https://github.com/masterada)
|
||||
* [xsbchen](https://github.com/xsbchen)
|
||||
* [Alex Harford](https://github.com/alexjh)
|
||||
* [Aleksandr Razumov](https://github.com/ernado)
|
||||
* [mchlrhw](https://github.com/mchlrhw)
|
||||
* [AlexWoo(武杰)](https://github.com/AlexWoo) *Fix RemoteDescription parsing for certificate fingerprint*
|
||||
* [Cecylia Bocovich](https://github.com/cohosh)
|
||||
* [Slugalisk](https://github.com/slugalisk)
|
||||
* [Agugua Kenechukwu](https://github.com/spaceCh1mp)
|
||||
* [Ato Araki](https://github.com/atotto)
|
||||
* [Rafael Viscarra](https://github.com/rviscarra)
|
||||
* [Mike Coleman](https://github.com/fivebats)
|
||||
* [Suhas Gaddam](https://github.com/suhasgaddam)
|
||||
* [Atsushi Watanabe](https://github.com/at-wat)
|
||||
* [Robert Eperjesi](https://github.com/epes)
|
||||
* [Aaron France](https://github.com/AeroNotix)
|
||||
* [Gareth Hayes](https://github.com/gazhayes)
|
||||
* [Sebastian Waisbrot](https://github.com/seppo0010)
|
||||
* [Masataka Hisasue](https://github.com/sylba2050) - *Fix Docs*
|
||||
* [Hongchao Ma(马洪超)](https://github.com/hcm007)
|
||||
* [Aaron France](https://github.com/AeroNotix)
|
||||
* [Chris Hiszpanski](https://github.com/thinkski) - *Fix Answer bundle generation*
|
||||
* [Vicken Simonian](https://github.com/vsimon)
|
||||
* [Guilherme Souza](https://github.com/gqgs)
|
||||
* [Andrew N. Shalaev](https://github.com/isqad)
|
||||
* [David Hamilton](https://github.com/dihamilton)
|
||||
* [Ilya Mayorov](https://github.com/faroyam)
|
||||
* [Patrick Lange](https://github.com/langep)
|
||||
* [cyannuk](https://github.com/cyannuk)
|
||||
* [Lukas Herman](https://github.com/lherman-cs)
|
||||
* [Konstantin Chugalinskiy](https://github.com/kchugalinskiy)
|
||||
* [Bao Nguyen](https://github.com/sysbot)
|
||||
* [Luke S](https://github.com/encounter)
|
||||
* [Hendrik Hofstadt](https://github.com/hendrikhofstadt)
|
||||
* [Clayton McCray](https://github.com/ClaytonMcCray)
|
||||
* [lawl](https://github.com/lawl)
|
||||
* [Jorropo](https://github.com/Jorropo)
|
||||
* [Akil](https://github.com/akilude)
|
||||
* [Quentin Renard](https://github.com/asticode)
|
||||
* [opennota](https://github.com/opennota)
|
||||
* [Simon Eisenmann](https://github.com/longsleep)
|
||||
* [Ben Weitzman](https://github.com/benweitzman)
|
||||
* [Masahiro Nakamura](https://github.com/tsuu32)
|
||||
* [Tarrence van As](https://github.com/tarrencev)
|
||||
* [Yuki Igarashi](https://github.com/bonprosoft)
|
||||
* [Egon Elbre](https://github.com/egonelbre)
|
||||
* [Jerko Steiner](https://github.com/jeremija)
|
||||
* [Roman Romanenko](https://github.com/r-novel)
|
||||
* [YongXin SHI](https://github.com/a-wing)
|
||||
* [Magnus Wahlstrand](https://github.com/kyeett)
|
||||
* [Chad Retz](https://github.com/cretz)
|
||||
* [Simone Gotti](https://github.com/sgotti)
|
||||
* [Cedric Fung](https://github.com/cedricfung)
|
||||
* [Norman Rasmussen](https://github.com/normanr) - *Fix Empty DataChannel messages*
|
||||
* [salmān aljammāz](https://github.com/saljam)
|
||||
* [cnderrauber](https://github.com/cnderrauber)
|
||||
* [Juliusz Chroboczek](https://github.com/jech)
|
||||
* [John Berthels](https://github.com/jbert)
|
||||
* [Somers Matthews](https://github.com/somersbmatthews)
|
||||
* [Vitaliy F](https://github.com/funvit)
|
||||
* [Ivan Egorov](https://github.com/vany-egorov)
|
||||
* [Nick Mykins](https://github.com/nmyk)
|
||||
* [Jason Brady](https://github.com/jbrady42)
|
||||
* [krishna chiatanya](https://github.com/kittuov)
|
||||
* [JacobZwang](https://github.com/JacobZwang)
|
||||
* [박종훈](https://github.com/JonghunBok)
|
||||
* [Sam Lancia](https://github.com/nerd2)
|
||||
* [Henry](https://github.com/cryptix)
|
||||
* [Jeff Tchang](https://github.com/tachang)
|
||||
* [JooYoung Lim](https://github.com/DevRockstarZ)
|
||||
* [Sidney San Martín](https://github.com/s4y)
|
||||
* [soolaugust](https://github.com/soolaugust)
|
||||
* [Kuzmin Vladimir](https://github.com/tekig)
|
||||
* [Alessandro Ros](https://github.com/aler9)
|
||||
* [Thomas Miller](https://github.com/tmiv)
|
||||
* [yoko(q191201771)](https://github.com/q191201771)
|
||||
* [Joshua Obasaju](https://github.com/obasajujoshua31)
|
||||
* [Mission Liao](https://github.com/mission-liao)
|
||||
* [Hanjun Kim](https://github.com/hallazzang)
|
||||
* [ZHENK](https://github.com/scorpionknifes)
|
||||
* [Rahul Nakre](https://github.com/rahulnakre)
|
||||
* [OrlandoCo](https://github.com/OrlandoCo)
|
||||
* [Assad Obaid](https://github.com/assadobaid)
|
||||
* [Jamie Good](https://github.com/jamiegood) - *Bug fix in jsfiddle example*
|
||||
* [Artur Shellunts](https://github.com/ashellunts)
|
||||
* [Sean Knight](https://github.com/SeanKnight)
|
||||
* [o0olele](https://github.com/o0olele)
|
||||
* [Bo Shi](https://github.com/bshimc)
|
||||
* [Suzuki Takeo](https://github.com/BambooTuna)
|
||||
* [baiyufei](https://github.com/baiyufei)
|
||||
* [pascal-ace](https://github.com/pascal-ace)
|
||||
* [Threadnaught](https://github.com/Threadnaught)
|
||||
* [Dean Eigenmann](https://github.com/decanus)
|
||||
|
||||
### License
|
||||
MIT License - see [LICENSE](LICENSE) for full text
|
73
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/api.go
generated
vendored
Normal file
73
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/api.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
// API bundles the global functions of the WebRTC and ORTC API.
|
||||
// Some of these functions are also exported globally using the
|
||||
// defaultAPI object. Note that the global version of the API
|
||||
// may be phased out in the future.
|
||||
type API struct {
|
||||
settingEngine *SettingEngine
|
||||
mediaEngine *MediaEngine
|
||||
interceptor interceptor.Interceptor
|
||||
}
|
||||
|
||||
// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
|
||||
func NewAPI(options ...func(*API)) *API {
|
||||
a := &API{}
|
||||
|
||||
for _, o := range options {
|
||||
o(a)
|
||||
}
|
||||
|
||||
if a.settingEngine == nil {
|
||||
a.settingEngine = &SettingEngine{}
|
||||
}
|
||||
|
||||
if a.settingEngine.LoggerFactory == nil {
|
||||
a.settingEngine.LoggerFactory = logging.NewDefaultLoggerFactory()
|
||||
}
|
||||
|
||||
if a.mediaEngine == nil {
|
||||
a.mediaEngine = &MediaEngine{}
|
||||
}
|
||||
|
||||
if a.interceptor == nil {
|
||||
a.interceptor = &interceptor.NoOp{}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// WithMediaEngine allows providing a MediaEngine to the API.
|
||||
// Settings can be changed after passing the engine to an API.
|
||||
func WithMediaEngine(m *MediaEngine) func(a *API) {
|
||||
return func(a *API) {
|
||||
if m != nil {
|
||||
a.mediaEngine = m
|
||||
} else {
|
||||
a.mediaEngine = &MediaEngine{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithSettingEngine allows providing a SettingEngine to the API.
|
||||
// Settings should not be changed after passing the engine to an API.
|
||||
func WithSettingEngine(s SettingEngine) func(a *API) {
|
||||
return func(a *API) {
|
||||
a.settingEngine = &s
|
||||
}
|
||||
}
|
||||
|
||||
// WithInterceptorRegistry allows providing Interceptors to the API.
|
||||
// Settings should not be changed after passing the registry to an API.
|
||||
func WithInterceptorRegistry(interceptorRegistry *interceptor.Registry) func(a *API) {
|
||||
return func(a *API) {
|
||||
a.interceptor = interceptorRegistry.Build()
|
||||
}
|
||||
}
|
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/api_js.go
generated
vendored
Normal file
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/api_js.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
// API bundles the global funcions of the WebRTC and ORTC API.
|
||||
type API struct {
|
||||
settingEngine *SettingEngine
|
||||
}
|
||||
|
||||
// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
|
||||
func NewAPI(options ...func(*API)) *API {
|
||||
a := &API{}
|
||||
|
||||
for _, o := range options {
|
||||
o(a)
|
||||
}
|
||||
|
||||
if a.settingEngine == nil {
|
||||
a.settingEngine = &SettingEngine{}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// WithSettingEngine allows providing a SettingEngine to the API.
|
||||
// Settings should not be changed after passing the engine to an API.
|
||||
func WithSettingEngine(s SettingEngine) func(a *API) {
|
||||
return func(a *API) {
|
||||
a.settingEngine = &s
|
||||
}
|
||||
}
|
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/atomicbool.go
generated
vendored
Normal file
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/atomicbool.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
package webrtc
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
type atomicBool struct {
|
||||
val int32
|
||||
}
|
||||
|
||||
func (b *atomicBool) set(value bool) { // nolint: unparam
|
||||
var i int32
|
||||
if value {
|
||||
i = 1
|
||||
}
|
||||
|
||||
atomic.StoreInt32(&(b.val), i)
|
||||
}
|
||||
|
||||
func (b *atomicBool) get() bool {
|
||||
return atomic.LoadInt32(&(b.val)) != 0
|
||||
}
|
78
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/bundlepolicy.go
generated
vendored
Normal file
78
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/bundlepolicy.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// BundlePolicy affects which media tracks are negotiated if the remote
|
||||
// endpoint is not bundle-aware, and what ICE candidates are gathered. If the
|
||||
// remote endpoint is bundle-aware, all media tracks and data channels are
|
||||
// bundled onto the same transport.
|
||||
type BundlePolicy int
|
||||
|
||||
const (
|
||||
// BundlePolicyBalanced indicates to gather ICE candidates for each
|
||||
// media type in use (audio, video, and data). If the remote endpoint is
|
||||
// not bundle-aware, negotiate only one audio and video track on separate
|
||||
// transports.
|
||||
BundlePolicyBalanced BundlePolicy = iota + 1
|
||||
|
||||
// BundlePolicyMaxCompat indicates to gather ICE candidates for each
|
||||
// track. If the remote endpoint is not bundle-aware, negotiate all media
|
||||
// tracks on separate transports.
|
||||
BundlePolicyMaxCompat
|
||||
|
||||
// BundlePolicyMaxBundle indicates to gather ICE candidates for only
|
||||
// one track. If the remote endpoint is not bundle-aware, negotiate only
|
||||
// one media track.
|
||||
BundlePolicyMaxBundle
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
bundlePolicyBalancedStr = "balanced"
|
||||
bundlePolicyMaxCompatStr = "max-compat"
|
||||
bundlePolicyMaxBundleStr = "max-bundle"
|
||||
)
|
||||
|
||||
func newBundlePolicy(raw string) BundlePolicy {
|
||||
switch raw {
|
||||
case bundlePolicyBalancedStr:
|
||||
return BundlePolicyBalanced
|
||||
case bundlePolicyMaxCompatStr:
|
||||
return BundlePolicyMaxCompat
|
||||
case bundlePolicyMaxBundleStr:
|
||||
return BundlePolicyMaxBundle
|
||||
default:
|
||||
return BundlePolicy(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t BundlePolicy) String() string {
|
||||
switch t {
|
||||
case BundlePolicyBalanced:
|
||||
return bundlePolicyBalancedStr
|
||||
case BundlePolicyMaxCompat:
|
||||
return bundlePolicyMaxCompatStr
|
||||
case BundlePolicyMaxBundle:
|
||||
return bundlePolicyMaxBundleStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (t *BundlePolicy) UnmarshalJSON(b []byte) error {
|
||||
var val string
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = newBundlePolicy(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (t BundlePolicy) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
185
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/certificate.go
generated
vendored
Normal file
185
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/certificate.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2/pkg/crypto/fingerprint"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// Certificate represents a x509Cert used to authenticate WebRTC communications.
|
||||
type Certificate struct {
|
||||
privateKey crypto.PrivateKey
|
||||
x509Cert *x509.Certificate
|
||||
statsID string
|
||||
}
|
||||
|
||||
// NewCertificate generates a new x509 compliant Certificate to be used
|
||||
// by DTLS for encrypting data sent over the wire. This method differs from
|
||||
// GenerateCertificate by allowing to specify a template x509.Certificate to
|
||||
// be used in order to define certificate parameters.
|
||||
func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certificate, error) {
|
||||
var err error
|
||||
var certDER []byte
|
||||
switch sk := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
pk := sk.Public()
|
||||
tpl.SignatureAlgorithm = x509.SHA256WithRSA
|
||||
certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
case *ecdsa.PrivateKey:
|
||||
pk := sk.Public()
|
||||
tpl.SignatureAlgorithm = x509.ECDSAWithSHA256
|
||||
certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
default:
|
||||
return nil, &rtcerr.NotSupportedError{Err: ErrPrivateKeyType}
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
|
||||
return &Certificate{privateKey: key, x509Cert: cert, statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano())}, nil
|
||||
}
|
||||
|
||||
// Equals determines if two certificates are identical by comparing both the
|
||||
// secretKeys and x509Certificates.
|
||||
func (c Certificate) Equals(o Certificate) bool {
|
||||
switch cSK := c.privateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
if oSK, ok := o.privateKey.(*rsa.PrivateKey); ok {
|
||||
if cSK.N.Cmp(oSK.N) != 0 {
|
||||
return false
|
||||
}
|
||||
return c.x509Cert.Equal(o.x509Cert)
|
||||
}
|
||||
return false
|
||||
case *ecdsa.PrivateKey:
|
||||
if oSK, ok := o.privateKey.(*ecdsa.PrivateKey); ok {
|
||||
if cSK.X.Cmp(oSK.X) != 0 || cSK.Y.Cmp(oSK.Y) != 0 {
|
||||
return false
|
||||
}
|
||||
return c.x509Cert.Equal(o.x509Cert)
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Expires returns the timestamp after which this certificate is no longer valid.
|
||||
func (c Certificate) Expires() time.Time {
|
||||
if c.x509Cert == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return c.x509Cert.NotAfter
|
||||
}
|
||||
|
||||
// GetFingerprints returns the list of certificate fingerprints, one of which
|
||||
// is computed with the digest algorithm used in the certificate signature.
|
||||
func (c Certificate) GetFingerprints() ([]DTLSFingerprint, error) {
|
||||
fingerprintAlgorithms := []crypto.Hash{crypto.SHA256}
|
||||
res := make([]DTLSFingerprint, len(fingerprintAlgorithms))
|
||||
|
||||
i := 0
|
||||
for _, algo := range fingerprintAlgorithms {
|
||||
name, err := fingerprint.StringFromHash(algo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err)
|
||||
}
|
||||
value, err := fingerprint.Fingerprint(c.x509Cert, algo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err)
|
||||
}
|
||||
res[i] = DTLSFingerprint{
|
||||
Algorithm: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
return res[:i+1], nil
|
||||
}
|
||||
|
||||
// GenerateCertificate causes the creation of an X.509 certificate and
|
||||
// corresponding private key.
|
||||
func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, error) {
|
||||
origin := make([]byte, 16)
|
||||
/* #nosec */
|
||||
if _, err := rand.Read(origin); err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
|
||||
// Max random value, a 130-bits integer, i.e 2^130 - 1
|
||||
maxBigInt := new(big.Int)
|
||||
/* #nosec */
|
||||
maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
|
||||
/* #nosec */
|
||||
serialNumber, err := rand.Int(rand.Reader, maxBigInt)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
|
||||
return NewCertificate(secretKey, x509.Certificate{
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageClientAuth,
|
||||
x509.ExtKeyUsageServerAuth,
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
NotBefore: time.Now(),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
NotAfter: time.Now().AddDate(0, 1, 0),
|
||||
SerialNumber: serialNumber,
|
||||
Version: 2,
|
||||
Subject: pkix.Name{CommonName: hex.EncodeToString(origin)},
|
||||
IsCA: true,
|
||||
})
|
||||
}
|
||||
|
||||
// CertificateFromX509 creates a new WebRTC Certificate from a given PrivateKey and Certificate
|
||||
//
|
||||
// This can be used if you want to share a certificate across multiple PeerConnections
|
||||
func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509.Certificate) Certificate {
|
||||
return Certificate{privateKey, certificate, fmt.Sprintf("certificate-%d", time.Now().UnixNano())}
|
||||
}
|
||||
|
||||
func (c Certificate) collectStats(report *statsReportCollector) error {
|
||||
report.Collecting()
|
||||
|
||||
fingerPrintAlgo, err := c.GetFingerprints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
base64Certificate := base64.RawURLEncoding.EncodeToString(c.x509Cert.Raw)
|
||||
|
||||
stats := CertificateStats{
|
||||
Timestamp: statsTimestampFrom(time.Now()),
|
||||
Type: StatsTypeCertificate,
|
||||
ID: c.statsID,
|
||||
Fingerprint: fingerPrintAlgo[0].Value,
|
||||
FingerprintAlgorithm: fingerPrintAlgo[0].Algorithm,
|
||||
Base64Certificate: base64Certificate,
|
||||
IssuerCertificateID: c.x509Cert.Issuer.String(),
|
||||
}
|
||||
|
||||
report.Collect(stats.ID, stats)
|
||||
return nil
|
||||
}
|
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/codecov.yml
generated
vendored
Normal file
20
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/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/**/*"
|
51
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration.go
generated
vendored
Normal file
51
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
// A Configuration defines how peer-to-peer communication via PeerConnection
|
||||
// is established or re-established.
|
||||
// Configurations may be set up once and reused across multiple connections.
|
||||
// Configurations are treated as readonly. As long as they are unmodified,
|
||||
// they are safe for concurrent use.
|
||||
type Configuration struct {
|
||||
// ICEServers defines a slice describing servers available to be used by
|
||||
// ICE, such as STUN and TURN servers.
|
||||
ICEServers []ICEServer `json:"iceServers,omitempty"`
|
||||
|
||||
// ICETransportPolicy indicates which candidates the ICEAgent is allowed
|
||||
// to use.
|
||||
ICETransportPolicy ICETransportPolicy `json:"iceTransportPolicy,omitempty"`
|
||||
|
||||
// BundlePolicy indicates which media-bundling policy to use when gathering
|
||||
// ICE candidates.
|
||||
BundlePolicy BundlePolicy `json:"bundlePolicy,omitempty"`
|
||||
|
||||
// RTCPMuxPolicy indicates which rtcp-mux policy to use when gathering ICE
|
||||
// candidates.
|
||||
RTCPMuxPolicy RTCPMuxPolicy `json:"rtcpMuxPolicy,omitempty"`
|
||||
|
||||
// PeerIdentity sets the target peer identity for the PeerConnection.
|
||||
// The PeerConnection will not establish a connection to a remote peer
|
||||
// unless it can be successfully authenticated with the provided name.
|
||||
PeerIdentity string `json:"peerIdentity"`
|
||||
|
||||
// Certificates describes a set of certificates that the PeerConnection
|
||||
// uses to authenticate. Valid values for this parameter are created
|
||||
// through calls to the GenerateCertificate function. Although any given
|
||||
// DTLS connection will use only one certificate, this attribute allows the
|
||||
// caller to provide multiple certificates that support different
|
||||
// algorithms. The final certificate will be selected based on the DTLS
|
||||
// handshake, which establishes which certificates are allowed. The
|
||||
// PeerConnection implementation selects which of the certificates is
|
||||
// used for a given connection; how certificates are selected is outside
|
||||
// the scope of this specification. If this value is absent, then a default
|
||||
// set of certificates is generated for each PeerConnection instance.
|
||||
Certificates []Certificate `json:"certificates,omitempty"`
|
||||
|
||||
// ICECandidatePoolSize describes the size of the prefetched ICE pool.
|
||||
ICECandidatePoolSize uint8 `json:"iceCandidatePoolSize,omitempty"`
|
||||
|
||||
// SDPSemantics controls the type of SDP offers accepted by and
|
||||
// SDP answers generated by the PeerConnection.
|
||||
SDPSemantics SDPSemantics `json:"sdpSemantics,omitempty"`
|
||||
}
|
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration_common.go
generated
vendored
Normal file
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration_common.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package webrtc
|
||||
|
||||
import "strings"
|
||||
|
||||
// getICEServers side-steps the strict parsing mode of the ice package
|
||||
// (as defined in https://tools.ietf.org/html/rfc7064) by copying and then
|
||||
// stripping any erroneous queries from "stun(s):" URLs before parsing.
|
||||
func (c Configuration) getICEServers() []ICEServer {
|
||||
iceServers := append([]ICEServer{}, c.ICEServers...)
|
||||
|
||||
for iceServersIndex := range iceServers {
|
||||
iceServers[iceServersIndex].URLs = append([]string{}, iceServers[iceServersIndex].URLs...)
|
||||
|
||||
for urlsIndex, rawURL := range iceServers[iceServersIndex].URLs {
|
||||
if strings.HasPrefix(rawURL, "stun") {
|
||||
// strip the query from "stun(s):" if present
|
||||
parts := strings.Split(rawURL, "?")
|
||||
rawURL = parts[0]
|
||||
}
|
||||
iceServers[iceServersIndex].URLs[urlsIndex] = rawURL
|
||||
}
|
||||
}
|
||||
return iceServers
|
||||
}
|
35
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration_js.go
generated
vendored
Normal file
35
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/configuration_js.go
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
// Configuration defines a set of parameters to configure how the
|
||||
// peer-to-peer communication via PeerConnection is established or
|
||||
// re-established.
|
||||
type Configuration struct {
|
||||
// ICEServers defines a slice describing servers available to be used by
|
||||
// ICE, such as STUN and TURN servers.
|
||||
ICEServers []ICEServer
|
||||
|
||||
// ICETransportPolicy indicates which candidates the ICEAgent is allowed
|
||||
// to use.
|
||||
ICETransportPolicy ICETransportPolicy
|
||||
|
||||
// BundlePolicy indicates which media-bundling policy to use when gathering
|
||||
// ICE candidates.
|
||||
BundlePolicy BundlePolicy
|
||||
|
||||
// RTCPMuxPolicy indicates which rtcp-mux policy to use when gathering ICE
|
||||
// candidates.
|
||||
RTCPMuxPolicy RTCPMuxPolicy
|
||||
|
||||
// PeerIdentity sets the target peer identity for the PeerConnection.
|
||||
// The PeerConnection will not establish a connection to a remote peer
|
||||
// unless it can be successfully authenticated with the provided name.
|
||||
PeerIdentity string
|
||||
|
||||
// Certificates are not supported in the JavaScript/Wasm bindings.
|
||||
// Certificates []Certificate
|
||||
|
||||
// ICECandidatePoolSize describes the size of the prefetched ICE pool.
|
||||
ICECandidatePoolSize uint8
|
||||
}
|
25
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/constants.go
generated
vendored
Normal file
25
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/constants.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package webrtc
|
||||
|
||||
const (
|
||||
// Unknown defines default public constant to use for "enum" like struct
|
||||
// comparisons when no value was defined.
|
||||
Unknown = iota
|
||||
unknownStr = "unknown"
|
||||
ssrcStr = "ssrc"
|
||||
|
||||
// Equal to UDP MTU
|
||||
receiveMTU = 1460
|
||||
|
||||
// simulcastProbeCount is the amount of RTP Packets
|
||||
// that handleUndeclaredSSRC will read and try to dispatch from
|
||||
// mid and rid values
|
||||
simulcastProbeCount = 10
|
||||
|
||||
// simulcastMaxProbeRoutines is how many active routines can be used to probe
|
||||
// If the total amount of incoming SSRCes exceeds this new requests will be ignored
|
||||
simulcastMaxProbeRoutines = 25
|
||||
|
||||
mediaSectionApplication = "application"
|
||||
|
||||
rtpOutboundMTU = 1200
|
||||
)
|
597
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel.go
generated
vendored
Normal file
597
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel.go
generated
vendored
Normal file
|
@ -0,0 +1,597 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/datachannel"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
const dataChannelBufferSize = math.MaxUint16 // message size limit for Chromium
|
||||
var errSCTPNotEstablished = errors.New("SCTP not established")
|
||||
|
||||
// DataChannel represents a WebRTC DataChannel
|
||||
// The DataChannel interface represents a network channel
|
||||
// which can be used for bidirectional peer-to-peer transfers of arbitrary data
|
||||
type DataChannel struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
statsID string
|
||||
label string
|
||||
ordered bool
|
||||
maxPacketLifeTime *uint16
|
||||
maxRetransmits *uint16
|
||||
protocol string
|
||||
negotiated bool
|
||||
id *uint16
|
||||
readyState atomic.Value // DataChannelState
|
||||
bufferedAmountLowThreshold uint64
|
||||
detachCalled bool
|
||||
|
||||
// The binaryType represents attribute MUST, on getting, return the value to
|
||||
// which it was last set. On setting, if the new value is either the string
|
||||
// "blob" or the string "arraybuffer", then set the IDL attribute to this
|
||||
// new value. Otherwise, throw a SyntaxError. When an DataChannel object
|
||||
// is created, the binaryType attribute MUST be initialized to the string
|
||||
// "blob". This attribute controls how binary data is exposed to scripts.
|
||||
// binaryType string
|
||||
|
||||
onMessageHandler func(DataChannelMessage)
|
||||
openHandlerOnce sync.Once
|
||||
onOpenHandler func()
|
||||
onCloseHandler func()
|
||||
onBufferedAmountLow func()
|
||||
onErrorHandler func(error)
|
||||
|
||||
sctpTransport *SCTPTransport
|
||||
dataChannel *datachannel.DataChannel
|
||||
|
||||
// A reference to the associated api object used by this datachannel
|
||||
api *API
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// NewDataChannel creates a new DataChannel.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewDataChannel(transport *SCTPTransport, params *DataChannelParameters) (*DataChannel, error) {
|
||||
d, err := api.newDataChannel(params, api.settingEngine.LoggerFactory.NewLogger("ortc"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.open(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// newDataChannel is an internal constructor for the data channel used to
|
||||
// create the DataChannel object before the networking is set up.
|
||||
func (api *API) newDataChannel(params *DataChannelParameters, log logging.LeveledLogger) (*DataChannel, error) {
|
||||
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5)
|
||||
if len(params.Label) > 65535 {
|
||||
return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit}
|
||||
}
|
||||
|
||||
d := &DataChannel{
|
||||
statsID: fmt.Sprintf("DataChannel-%d", time.Now().UnixNano()),
|
||||
label: params.Label,
|
||||
protocol: params.Protocol,
|
||||
negotiated: params.Negotiated,
|
||||
id: params.ID,
|
||||
ordered: params.Ordered,
|
||||
maxPacketLifeTime: params.MaxPacketLifeTime,
|
||||
maxRetransmits: params.MaxRetransmits,
|
||||
api: api,
|
||||
log: log,
|
||||
}
|
||||
|
||||
d.setReadyState(DataChannelStateConnecting)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// open opens the datachannel over the sctp transport
|
||||
func (d *DataChannel) open(sctpTransport *SCTPTransport) error {
|
||||
d.mu.Lock()
|
||||
if d.sctpTransport != nil {
|
||||
// already open
|
||||
d.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
d.sctpTransport = sctpTransport
|
||||
|
||||
if err := d.ensureSCTP(); err != nil {
|
||||
d.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
var channelType datachannel.ChannelType
|
||||
var reliabilityParameter uint32
|
||||
|
||||
switch {
|
||||
case d.maxPacketLifeTime == nil && d.maxRetransmits == nil:
|
||||
if d.ordered {
|
||||
channelType = datachannel.ChannelTypeReliable
|
||||
} else {
|
||||
channelType = datachannel.ChannelTypeReliableUnordered
|
||||
}
|
||||
|
||||
case d.maxRetransmits != nil:
|
||||
reliabilityParameter = uint32(*d.maxRetransmits)
|
||||
if d.ordered {
|
||||
channelType = datachannel.ChannelTypePartialReliableRexmit
|
||||
} else {
|
||||
channelType = datachannel.ChannelTypePartialReliableRexmitUnordered
|
||||
}
|
||||
default:
|
||||
reliabilityParameter = uint32(*d.maxPacketLifeTime)
|
||||
if d.ordered {
|
||||
channelType = datachannel.ChannelTypePartialReliableTimed
|
||||
} else {
|
||||
channelType = datachannel.ChannelTypePartialReliableTimedUnordered
|
||||
}
|
||||
}
|
||||
|
||||
cfg := &datachannel.Config{
|
||||
ChannelType: channelType,
|
||||
Priority: datachannel.ChannelPriorityNormal,
|
||||
ReliabilityParameter: reliabilityParameter,
|
||||
Label: d.label,
|
||||
Protocol: d.protocol,
|
||||
Negotiated: d.negotiated,
|
||||
LoggerFactory: d.api.settingEngine.LoggerFactory,
|
||||
}
|
||||
|
||||
if d.id == nil {
|
||||
err := d.sctpTransport.generateAndSetDataChannelID(d.sctpTransport.dtlsTransport.role(), &d.id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dc, err := datachannel.Dial(d.sctpTransport.association, *d.id, cfg)
|
||||
if err != nil {
|
||||
d.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// bufferedAmountLowThreshold and onBufferedAmountLow might be set earlier
|
||||
dc.SetBufferedAmountLowThreshold(d.bufferedAmountLowThreshold)
|
||||
dc.OnBufferedAmountLow(d.onBufferedAmountLow)
|
||||
d.mu.Unlock()
|
||||
|
||||
d.handleOpen(dc)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DataChannel) ensureSCTP() error {
|
||||
if d.sctpTransport == nil {
|
||||
return errSCTPNotEstablished
|
||||
}
|
||||
|
||||
d.sctpTransport.lock.RLock()
|
||||
defer d.sctpTransport.lock.RUnlock()
|
||||
if d.sctpTransport.association == nil {
|
||||
return errSCTPNotEstablished
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transport returns the SCTPTransport instance the DataChannel is sending over.
|
||||
func (d *DataChannel) Transport() *SCTPTransport {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.sctpTransport
|
||||
}
|
||||
|
||||
// After onOpen is complete check that the user called detach
|
||||
// and provide an error message if the call was missed
|
||||
func (d *DataChannel) checkDetachAfterOpen() {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
if d.api.settingEngine.detach.DataChannels && !d.detachCalled {
|
||||
d.log.Warn("webrtc.DetachDataChannels() enabled but didn't Detach, call Detach from OnOpen")
|
||||
}
|
||||
}
|
||||
|
||||
// OnOpen sets an event handler which is invoked when
|
||||
// the underlying data transport has been established (or re-established).
|
||||
func (d *DataChannel) OnOpen(f func()) {
|
||||
d.mu.Lock()
|
||||
d.openHandlerOnce = sync.Once{}
|
||||
d.onOpenHandler = f
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.ReadyState() == DataChannelStateOpen {
|
||||
// If the data channel is already open, call the handler immediately.
|
||||
go d.openHandlerOnce.Do(func() {
|
||||
f()
|
||||
d.checkDetachAfterOpen()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataChannel) onOpen() {
|
||||
d.mu.RLock()
|
||||
handler := d.onOpenHandler
|
||||
d.mu.RUnlock()
|
||||
|
||||
if handler != nil {
|
||||
go d.openHandlerOnce.Do(func() {
|
||||
handler()
|
||||
d.checkDetachAfterOpen()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// OnClose sets an event handler which is invoked when
|
||||
// the underlying data transport has been closed.
|
||||
func (d *DataChannel) OnClose(f func()) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.onCloseHandler = f
|
||||
}
|
||||
|
||||
func (d *DataChannel) onClose() {
|
||||
d.mu.RLock()
|
||||
handler := d.onCloseHandler
|
||||
d.mu.RUnlock()
|
||||
|
||||
if handler != nil {
|
||||
go handler()
|
||||
}
|
||||
}
|
||||
|
||||
// OnMessage sets an event handler which is invoked on a binary
|
||||
// message arrival over the sctp transport from a remote peer.
|
||||
// OnMessage can currently receive messages up to 16384 bytes
|
||||
// in size. Check out the detach API if you want to use larger
|
||||
// message sizes. Note that browser support for larger messages
|
||||
// is also limited.
|
||||
func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.onMessageHandler = f
|
||||
}
|
||||
|
||||
func (d *DataChannel) onMessage(msg DataChannelMessage) {
|
||||
d.mu.RLock()
|
||||
handler := d.onMessageHandler
|
||||
d.mu.RUnlock()
|
||||
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
handler(msg)
|
||||
}
|
||||
|
||||
func (d *DataChannel) handleOpen(dc *datachannel.DataChannel) {
|
||||
d.mu.Lock()
|
||||
d.dataChannel = dc
|
||||
d.mu.Unlock()
|
||||
d.setReadyState(DataChannelStateOpen)
|
||||
|
||||
d.onOpen()
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if !d.api.settingEngine.detach.DataChannels {
|
||||
go d.readLoop()
|
||||
}
|
||||
}
|
||||
|
||||
// OnError sets an event handler which is invoked when
|
||||
// the underlying data transport cannot be read.
|
||||
func (d *DataChannel) OnError(f func(err error)) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
d.onErrorHandler = f
|
||||
}
|
||||
|
||||
func (d *DataChannel) onError(err error) {
|
||||
d.mu.RLock()
|
||||
handler := d.onErrorHandler
|
||||
d.mu.RUnlock()
|
||||
|
||||
if handler != nil {
|
||||
go handler(err)
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/pion/webrtc/issues/1516
|
||||
// nolint:gochecknoglobals
|
||||
var rlBufPool = sync.Pool{New: func() interface{} {
|
||||
return make([]byte, dataChannelBufferSize)
|
||||
}}
|
||||
|
||||
func (d *DataChannel) readLoop() {
|
||||
for {
|
||||
buffer := rlBufPool.Get().([]byte)
|
||||
n, isString, err := d.dataChannel.ReadDataChannel(buffer)
|
||||
if err != nil {
|
||||
rlBufPool.Put(buffer) // nolint:staticcheck
|
||||
d.setReadyState(DataChannelStateClosed)
|
||||
if err != io.EOF {
|
||||
d.onError(err)
|
||||
}
|
||||
d.onClose()
|
||||
return
|
||||
}
|
||||
|
||||
m := DataChannelMessage{Data: make([]byte, n), IsString: isString}
|
||||
copy(m.Data, buffer[:n])
|
||||
// The 'staticcheck' pragma is a false positive on the part of the CI linter.
|
||||
rlBufPool.Put(buffer) // nolint:staticcheck
|
||||
|
||||
// NB: Why was DataChannelMessage not passed as a pointer value?
|
||||
d.onMessage(m) // nolint:staticcheck
|
||||
}
|
||||
}
|
||||
|
||||
// Send sends the binary message to the DataChannel peer
|
||||
func (d *DataChannel) Send(data []byte) error {
|
||||
err := d.ensureOpen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.dataChannel.WriteDataChannel(data, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// SendText sends the text message to the DataChannel peer
|
||||
func (d *DataChannel) SendText(s string) error {
|
||||
err := d.ensureOpen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = d.dataChannel.WriteDataChannel([]byte(s), true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *DataChannel) ensureOpen() error {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
if d.ReadyState() != DataChannelStateOpen {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Detach allows you to detach the underlying datachannel. This provides
|
||||
// an idiomatic API to work with, however it disables the OnMessage callback.
|
||||
// Before calling Detach you have to enable this behavior by calling
|
||||
// webrtc.DetachDataChannels(). Combining detached and normal data channels
|
||||
// is not supported.
|
||||
// Please refer to the data-channels-detach example and the
|
||||
// pion/datachannel documentation for the correct way to handle the
|
||||
// resulting DataChannel object.
|
||||
func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
if !d.api.settingEngine.detach.DataChannels {
|
||||
return nil, errDetachNotEnabled
|
||||
}
|
||||
|
||||
if d.dataChannel == nil {
|
||||
return nil, errDetachBeforeOpened
|
||||
}
|
||||
|
||||
d.detachCalled = true
|
||||
|
||||
return d.dataChannel, nil
|
||||
}
|
||||
|
||||
// Close Closes the DataChannel. It may be called regardless of whether
|
||||
// the DataChannel object was created by this peer or the remote peer.
|
||||
func (d *DataChannel) Close() error {
|
||||
d.mu.Lock()
|
||||
haveSctpTransport := d.dataChannel != nil
|
||||
d.mu.Unlock()
|
||||
|
||||
if d.ReadyState() == DataChannelStateClosed {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.setReadyState(DataChannelStateClosing)
|
||||
if !haveSctpTransport {
|
||||
return nil
|
||||
}
|
||||
|
||||
return d.dataChannel.Close()
|
||||
}
|
||||
|
||||
// Label represents a label that can be used to distinguish this
|
||||
// DataChannel object from other DataChannel objects. Scripts are
|
||||
// allowed to create multiple DataChannel objects with the same label.
|
||||
func (d *DataChannel) Label() string {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.label
|
||||
}
|
||||
|
||||
// Ordered represents if the DataChannel is ordered, and false if
|
||||
// out-of-order delivery is allowed.
|
||||
func (d *DataChannel) Ordered() bool {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.ordered
|
||||
}
|
||||
|
||||
// MaxPacketLifeTime represents the length of the time window (msec) during
|
||||
// which transmissions and retransmissions may occur in unreliable mode.
|
||||
func (d *DataChannel) MaxPacketLifeTime() *uint16 {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.maxPacketLifeTime
|
||||
}
|
||||
|
||||
// MaxRetransmits represents the maximum number of retransmissions that are
|
||||
// attempted in unreliable mode.
|
||||
func (d *DataChannel) MaxRetransmits() *uint16 {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.maxRetransmits
|
||||
}
|
||||
|
||||
// Protocol represents the name of the sub-protocol used with this
|
||||
// DataChannel.
|
||||
func (d *DataChannel) Protocol() string {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.protocol
|
||||
}
|
||||
|
||||
// Negotiated represents whether this DataChannel was negotiated by the
|
||||
// application (true), or not (false).
|
||||
func (d *DataChannel) Negotiated() bool {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.negotiated
|
||||
}
|
||||
|
||||
// ID represents the ID for this DataChannel. The value is initially
|
||||
// null, which is what will be returned if the ID was not provided at
|
||||
// channel creation time, and the DTLS role of the SCTP transport has not
|
||||
// yet been negotiated. Otherwise, it will return the ID that was either
|
||||
// selected by the script or generated. After the ID is set to a non-null
|
||||
// value, it will not change.
|
||||
func (d *DataChannel) ID() *uint16 {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
return d.id
|
||||
}
|
||||
|
||||
// ReadyState represents the state of the DataChannel object.
|
||||
func (d *DataChannel) ReadyState() DataChannelState {
|
||||
if v := d.readyState.Load(); v != nil {
|
||||
return v.(DataChannelState)
|
||||
}
|
||||
return DataChannelState(0)
|
||||
}
|
||||
|
||||
// BufferedAmount represents the number of bytes of application data
|
||||
// (UTF-8 text and binary data) that have been queued using send(). Even
|
||||
// though the data transmission can occur in parallel, the returned value
|
||||
// MUST NOT be decreased before the current task yielded back to the event
|
||||
// loop to prevent race conditions. The value does not include framing
|
||||
// overhead incurred by the protocol, or buffering done by the operating
|
||||
// system or network hardware. The value of BufferedAmount slot will only
|
||||
// increase with each call to the send() method as long as the ReadyState is
|
||||
// open; however, BufferedAmount does not reset to zero once the channel
|
||||
// closes.
|
||||
func (d *DataChannel) BufferedAmount() uint64 {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
if d.dataChannel == nil {
|
||||
return 0
|
||||
}
|
||||
return d.dataChannel.BufferedAmount()
|
||||
}
|
||||
|
||||
// BufferedAmountLowThreshold represents the threshold at which the
|
||||
// bufferedAmount is considered to be low. When the bufferedAmount decreases
|
||||
// from above this threshold to equal or below it, the bufferedamountlow
|
||||
// event fires. BufferedAmountLowThreshold is initially zero on each new
|
||||
// DataChannel, but the application may change its value at any time.
|
||||
// The threshold is set to 0 by default.
|
||||
func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
|
||||
if d.dataChannel == nil {
|
||||
return d.bufferedAmountLowThreshold
|
||||
}
|
||||
return d.dataChannel.BufferedAmountLowThreshold()
|
||||
}
|
||||
|
||||
// SetBufferedAmountLowThreshold is used to update the threshold.
|
||||
// See BufferedAmountLowThreshold().
|
||||
func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
d.bufferedAmountLowThreshold = th
|
||||
|
||||
if d.dataChannel != nil {
|
||||
d.dataChannel.SetBufferedAmountLowThreshold(th)
|
||||
}
|
||||
}
|
||||
|
||||
// OnBufferedAmountLow sets an event handler which is invoked when
|
||||
// the number of bytes of outgoing data becomes lower than the
|
||||
// BufferedAmountLowThreshold.
|
||||
func (d *DataChannel) OnBufferedAmountLow(f func()) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
d.onBufferedAmountLow = f
|
||||
if d.dataChannel != nil {
|
||||
d.dataChannel.OnBufferedAmountLow(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DataChannel) getStatsID() string {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
return d.statsID
|
||||
}
|
||||
|
||||
func (d *DataChannel) collectStats(collector *statsReportCollector) {
|
||||
collector.Collecting()
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
|
||||
stats := DataChannelStats{
|
||||
Timestamp: statsTimestampNow(),
|
||||
Type: StatsTypeDataChannel,
|
||||
ID: d.statsID,
|
||||
Label: d.label,
|
||||
Protocol: d.protocol,
|
||||
// TransportID string `json:"transportId"`
|
||||
State: d.ReadyState(),
|
||||
}
|
||||
|
||||
if d.id != nil {
|
||||
stats.DataChannelIdentifier = int32(*d.id)
|
||||
}
|
||||
|
||||
if d.dataChannel != nil {
|
||||
stats.MessagesSent = d.dataChannel.MessagesSent()
|
||||
stats.BytesSent = d.dataChannel.BytesSent()
|
||||
stats.MessagesReceived = d.dataChannel.MessagesReceived()
|
||||
stats.BytesReceived = d.dataChannel.BytesReceived()
|
||||
}
|
||||
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
|
||||
func (d *DataChannel) setReadyState(r DataChannelState) {
|
||||
d.readyState.Store(r)
|
||||
}
|
319
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel_js.go
generated
vendored
Normal file
319
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel_js.go
generated
vendored
Normal file
|
@ -0,0 +1,319 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/pion/datachannel"
|
||||
)
|
||||
|
||||
const dataChannelBufferSize = 16384 // Lowest common denominator among browsers
|
||||
|
||||
// DataChannel represents a WebRTC DataChannel
|
||||
// The DataChannel interface represents a network channel
|
||||
// which can be used for bidirectional peer-to-peer transfers of arbitrary data
|
||||
type DataChannel struct {
|
||||
// Pointer to the underlying JavaScript RTCPeerConnection object.
|
||||
underlying js.Value
|
||||
|
||||
// Keep track of handlers/callbacks so we can call Release as required by the
|
||||
// syscall/js API. Initially nil.
|
||||
onOpenHandler *js.Func
|
||||
onCloseHandler *js.Func
|
||||
onMessageHandler *js.Func
|
||||
onBufferedAmountLow *js.Func
|
||||
|
||||
// A reference to the associated api object used by this datachannel
|
||||
api *API
|
||||
}
|
||||
|
||||
// OnOpen sets an event handler which is invoked when
|
||||
// the underlying data transport has been established (or re-established).
|
||||
func (d *DataChannel) OnOpen(f func()) {
|
||||
if d.onOpenHandler != nil {
|
||||
oldHandler := d.onOpenHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onOpenHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go f()
|
||||
return js.Undefined()
|
||||
})
|
||||
d.onOpenHandler = &onOpenHandler
|
||||
d.underlying.Set("onopen", onOpenHandler)
|
||||
}
|
||||
|
||||
// OnClose sets an event handler which is invoked when
|
||||
// the underlying data transport has been closed.
|
||||
func (d *DataChannel) OnClose(f func()) {
|
||||
if d.onCloseHandler != nil {
|
||||
oldHandler := d.onCloseHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onCloseHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go f()
|
||||
return js.Undefined()
|
||||
})
|
||||
d.onCloseHandler = &onCloseHandler
|
||||
d.underlying.Set("onclose", onCloseHandler)
|
||||
}
|
||||
|
||||
// OnMessage sets an event handler which is invoked on a binary message arrival
|
||||
// from a remote peer. Note that browsers may place limitations on message size.
|
||||
func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
|
||||
if d.onMessageHandler != nil {
|
||||
oldHandler := d.onMessageHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onMessageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
// pion/webrtc/projects/15
|
||||
data := args[0].Get("data")
|
||||
go func() {
|
||||
// valueToDataChannelMessage may block when handling 'Blob' data
|
||||
// so we need to call it from a new routine. See:
|
||||
// https://pkg.go.dev/syscall/js#FuncOf
|
||||
msg := valueToDataChannelMessage(data)
|
||||
f(msg)
|
||||
}()
|
||||
return js.Undefined()
|
||||
})
|
||||
d.onMessageHandler = &onMessageHandler
|
||||
d.underlying.Set("onmessage", onMessageHandler)
|
||||
}
|
||||
|
||||
// Send sends the binary message to the DataChannel peer
|
||||
func (d *DataChannel) Send(data []byte) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
array := js.Global().Get("Uint8Array").New(len(data))
|
||||
js.CopyBytesToJS(array, data)
|
||||
d.underlying.Call("send", array)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendText sends the text message to the DataChannel peer
|
||||
func (d *DataChannel) SendText(s string) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
d.underlying.Call("send", s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Detach allows you to detach the underlying datachannel. This provides
|
||||
// an idiomatic API to work with, however it disables the OnMessage callback.
|
||||
// Before calling Detach you have to enable this behavior by calling
|
||||
// webrtc.DetachDataChannels(). Combining detached and normal data channels
|
||||
// is not supported.
|
||||
// Please reffer to the data-channels-detach example and the
|
||||
// pion/datachannel documentation for the correct way to handle the
|
||||
// resulting DataChannel object.
|
||||
func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
|
||||
if !d.api.settingEngine.detach.DataChannels {
|
||||
return nil, fmt.Errorf("enable detaching by calling webrtc.DetachDataChannels()")
|
||||
}
|
||||
|
||||
detached := newDetachedDataChannel(d)
|
||||
return detached, nil
|
||||
}
|
||||
|
||||
// Close Closes the DataChannel. It may be called regardless of whether
|
||||
// the DataChannel object was created by this peer or the remote peer.
|
||||
func (d *DataChannel) Close() (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
|
||||
d.underlying.Call("close")
|
||||
|
||||
// Release any handlers as required by the syscall/js API.
|
||||
if d.onOpenHandler != nil {
|
||||
d.onOpenHandler.Release()
|
||||
}
|
||||
if d.onCloseHandler != nil {
|
||||
d.onCloseHandler.Release()
|
||||
}
|
||||
if d.onMessageHandler != nil {
|
||||
d.onMessageHandler.Release()
|
||||
}
|
||||
if d.onBufferedAmountLow != nil {
|
||||
d.onBufferedAmountLow.Release()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Label represents a label that can be used to distinguish this
|
||||
// DataChannel object from other DataChannel objects. Scripts are
|
||||
// allowed to create multiple DataChannel objects with the same label.
|
||||
func (d *DataChannel) Label() string {
|
||||
return d.underlying.Get("label").String()
|
||||
}
|
||||
|
||||
// Ordered represents if the DataChannel is ordered, and false if
|
||||
// out-of-order delivery is allowed.
|
||||
func (d *DataChannel) Ordered() bool {
|
||||
ordered := d.underlying.Get("ordered")
|
||||
if jsValueIsUndefined(ordered) {
|
||||
return true // default is true
|
||||
}
|
||||
return ordered.Bool()
|
||||
}
|
||||
|
||||
// MaxPacketLifeTime represents the length of the time window (msec) during
|
||||
// which transmissions and retransmissions may occur in unreliable mode.
|
||||
func (d *DataChannel) MaxPacketLifeTime() *uint16 {
|
||||
if !jsValueIsUndefined(d.underlying.Get("maxPacketLifeTime")) {
|
||||
return valueToUint16Pointer(d.underlying.Get("maxPacketLifeTime"))
|
||||
} else {
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
|
||||
// Chrome calls this "maxRetransmitTime"
|
||||
return valueToUint16Pointer(d.underlying.Get("maxRetransmitTime"))
|
||||
}
|
||||
}
|
||||
|
||||
// MaxRetransmits represents the maximum number of retransmissions that are
|
||||
// attempted in unreliable mode.
|
||||
func (d *DataChannel) MaxRetransmits() *uint16 {
|
||||
return valueToUint16Pointer(d.underlying.Get("maxRetransmits"))
|
||||
}
|
||||
|
||||
// Protocol represents the name of the sub-protocol used with this
|
||||
// DataChannel.
|
||||
func (d *DataChannel) Protocol() string {
|
||||
return d.underlying.Get("protocol").String()
|
||||
}
|
||||
|
||||
// Negotiated represents whether this DataChannel was negotiated by the
|
||||
// application (true), or not (false).
|
||||
func (d *DataChannel) Negotiated() bool {
|
||||
return d.underlying.Get("negotiated").Bool()
|
||||
}
|
||||
|
||||
// ID represents the ID for this DataChannel. The value is initially
|
||||
// null, which is what will be returned if the ID was not provided at
|
||||
// channel creation time. Otherwise, it will return the ID that was either
|
||||
// selected by the script or generated. After the ID is set to a non-null
|
||||
// value, it will not change.
|
||||
func (d *DataChannel) ID() *uint16 {
|
||||
return valueToUint16Pointer(d.underlying.Get("id"))
|
||||
}
|
||||
|
||||
// ReadyState represents the state of the DataChannel object.
|
||||
func (d *DataChannel) ReadyState() DataChannelState {
|
||||
return newDataChannelState(d.underlying.Get("readyState").String())
|
||||
}
|
||||
|
||||
// BufferedAmount represents the number of bytes of application data
|
||||
// (UTF-8 text and binary data) that have been queued using send(). Even
|
||||
// though the data transmission can occur in parallel, the returned value
|
||||
// MUST NOT be decreased before the current task yielded back to the event
|
||||
// loop to prevent race conditions. The value does not include framing
|
||||
// overhead incurred by the protocol, or buffering done by the operating
|
||||
// system or network hardware. The value of BufferedAmount slot will only
|
||||
// increase with each call to the send() method as long as the ReadyState is
|
||||
// open; however, BufferedAmount does not reset to zero once the channel
|
||||
// closes.
|
||||
func (d *DataChannel) BufferedAmount() uint64 {
|
||||
return uint64(d.underlying.Get("bufferedAmount").Int())
|
||||
}
|
||||
|
||||
// BufferedAmountLowThreshold represents the threshold at which the
|
||||
// bufferedAmount is considered to be low. When the bufferedAmount decreases
|
||||
// from above this threshold to equal or below it, the bufferedamountlow
|
||||
// event fires. BufferedAmountLowThreshold is initially zero on each new
|
||||
// DataChannel, but the application may change its value at any time.
|
||||
func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
|
||||
return uint64(d.underlying.Get("bufferedAmountLowThreshold").Int())
|
||||
}
|
||||
|
||||
// SetBufferedAmountLowThreshold is used to update the threshold.
|
||||
// See BufferedAmountLowThreshold().
|
||||
func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
|
||||
d.underlying.Set("bufferedAmountLowThreshold", th)
|
||||
}
|
||||
|
||||
// OnBufferedAmountLow sets an event handler which is invoked when
|
||||
// the number of bytes of outgoing data becomes lower than the
|
||||
// BufferedAmountLowThreshold.
|
||||
func (d *DataChannel) OnBufferedAmountLow(f func()) {
|
||||
if d.onBufferedAmountLow != nil {
|
||||
oldHandler := d.onBufferedAmountLow
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onBufferedAmountLow := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go f()
|
||||
return js.Undefined()
|
||||
})
|
||||
d.onBufferedAmountLow = &onBufferedAmountLow
|
||||
d.underlying.Set("onbufferedamountlow", onBufferedAmountLow)
|
||||
}
|
||||
|
||||
// valueToDataChannelMessage converts the given value to a DataChannelMessage.
|
||||
// val should be obtained from MessageEvent.data where MessageEvent is received
|
||||
// via the RTCDataChannel.onmessage callback.
|
||||
func valueToDataChannelMessage(val js.Value) DataChannelMessage {
|
||||
// If val is of type string, the conversion is straightforward.
|
||||
if val.Type() == js.TypeString {
|
||||
return DataChannelMessage{
|
||||
IsString: true,
|
||||
Data: []byte(val.String()),
|
||||
}
|
||||
}
|
||||
|
||||
// For other types, we need to first determine val.constructor.name.
|
||||
constructorName := val.Get("constructor").Get("name").String()
|
||||
var data []byte
|
||||
switch constructorName {
|
||||
case "Uint8Array":
|
||||
// We can easily convert Uint8Array to []byte
|
||||
data = uint8ArrayValueToBytes(val)
|
||||
case "Blob":
|
||||
// Convert the Blob to an ArrayBuffer and then convert the ArrayBuffer
|
||||
// to a Uint8Array.
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||||
|
||||
// The JavaScript API for reading from the Blob is asynchronous. We use a
|
||||
// channel to signal when reading is done.
|
||||
reader := js.Global().Get("FileReader").New()
|
||||
doneChan := make(chan struct{})
|
||||
reader.Call("addEventListener", "loadend", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
// Signal that the FileReader is done reading/loading by sending through
|
||||
// the doneChan.
|
||||
doneChan <- struct{}{}
|
||||
}()
|
||||
return js.Undefined()
|
||||
}))
|
||||
|
||||
reader.Call("readAsArrayBuffer", val)
|
||||
|
||||
// Wait for the FileReader to finish reading/loading.
|
||||
<-doneChan
|
||||
|
||||
// At this point buffer.result is a typed array, which we know how to
|
||||
// handle.
|
||||
buffer := reader.Get("result")
|
||||
uint8Array := js.Global().Get("Uint8Array").New(buffer)
|
||||
data = uint8ArrayValueToBytes(uint8Array)
|
||||
default:
|
||||
// Assume we have an ArrayBufferView type which we can convert to a
|
||||
// Uint8Array in JavaScript.
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
|
||||
uint8Array := js.Global().Get("Uint8Array").New(val)
|
||||
data = uint8ArrayValueToBytes(uint8Array)
|
||||
}
|
||||
|
||||
return DataChannelMessage{
|
||||
IsString: false,
|
||||
Data: data,
|
||||
}
|
||||
}
|
71
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go
generated
vendored
Normal file
71
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type detachedDataChannel struct {
|
||||
dc *DataChannel
|
||||
|
||||
read chan DataChannelMessage
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newDetachedDataChannel(dc *DataChannel) *detachedDataChannel {
|
||||
read := make(chan DataChannelMessage)
|
||||
done := make(chan struct{})
|
||||
|
||||
// Wire up callbacks
|
||||
dc.OnMessage(func(msg DataChannelMessage) {
|
||||
read <- msg // pion/webrtc/projects/15
|
||||
})
|
||||
|
||||
// pion/webrtc/projects/15
|
||||
|
||||
return &detachedDataChannel{
|
||||
dc: dc,
|
||||
read: read,
|
||||
done: done,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *detachedDataChannel) Read(p []byte) (int, error) {
|
||||
n, _, err := c.ReadDataChannel(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *detachedDataChannel) ReadDataChannel(p []byte) (int, bool, error) {
|
||||
select {
|
||||
case <-c.done:
|
||||
return 0, false, errors.New("Reader closed")
|
||||
case msg := <-c.read:
|
||||
n := copy(p, msg.Data)
|
||||
if n < len(msg.Data) {
|
||||
return n, msg.IsString, errors.New("Read buffer to small")
|
||||
}
|
||||
return n, msg.IsString, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *detachedDataChannel) Write(p []byte) (n int, err error) {
|
||||
return c.WriteDataChannel(p, false)
|
||||
}
|
||||
|
||||
func (c *detachedDataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) {
|
||||
if isString {
|
||||
err = c.dc.SendText(string(p))
|
||||
return len(p), err
|
||||
}
|
||||
|
||||
err = c.dc.Send(p)
|
||||
|
||||
return len(p), err
|
||||
}
|
||||
|
||||
func (c *detachedDataChannel) Close() error {
|
||||
close(c.done)
|
||||
|
||||
return c.dc.Close()
|
||||
}
|
33
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelinit.go
generated
vendored
Normal file
33
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelinit.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package webrtc
|
||||
|
||||
// DataChannelInit can be used to configure properties of the underlying
|
||||
// channel such as data reliability.
|
||||
type DataChannelInit struct {
|
||||
// Ordered indicates if data is allowed to be delivered out of order. The
|
||||
// default value of true, guarantees that data will be delivered in order.
|
||||
Ordered *bool
|
||||
|
||||
// MaxPacketLifeTime limits the time (in milliseconds) during which the
|
||||
// channel will transmit or retransmit data if not acknowledged. This value
|
||||
// may be clamped if it exceeds the maximum value supported.
|
||||
MaxPacketLifeTime *uint16
|
||||
|
||||
// MaxRetransmits limits the number of times a channel will retransmit data
|
||||
// if not successfully delivered. This value may be clamped if it exceeds
|
||||
// the maximum value supported.
|
||||
MaxRetransmits *uint16
|
||||
|
||||
// Protocol describes the subprotocol name used for this channel.
|
||||
Protocol *string
|
||||
|
||||
// Negotiated describes if the data channel is created by the local peer or
|
||||
// the remote peer. The default value of false tells the user agent to
|
||||
// announce the channel in-band and instruct the other peer to dispatch a
|
||||
// corresponding DataChannel. If set to true, it is up to the application
|
||||
// to negotiate the channel and create an DataChannel with the same id
|
||||
// at the other peer.
|
||||
Negotiated *bool
|
||||
|
||||
// ID overrides the default selection of ID for this channel.
|
||||
ID *uint16
|
||||
}
|
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelmessage.go
generated
vendored
Normal file
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelmessage.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package webrtc
|
||||
|
||||
// DataChannelMessage represents a message received from the
|
||||
// data channel. IsString will be set to true if the incoming
|
||||
// message is of the string type. Otherwise the message is of
|
||||
// a binary type.
|
||||
type DataChannelMessage struct {
|
||||
IsString bool
|
||||
Data []byte
|
||||
}
|
12
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelparameters.go
generated
vendored
Normal file
12
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package webrtc
|
||||
|
||||
// DataChannelParameters describes the configuration of the DataChannel.
|
||||
type DataChannelParameters struct {
|
||||
Label string `json:"label"`
|
||||
Protocol string `json:"protocol"`
|
||||
ID *uint16 `json:"id"`
|
||||
Ordered bool `json:"ordered"`
|
||||
MaxPacketLifeTime *uint16 `json:"maxPacketLifeTime"`
|
||||
MaxRetransmits *uint16 `json:"maxRetransmits"`
|
||||
Negotiated bool `json:"negotiated"`
|
||||
}
|
61
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelstate.go
generated
vendored
Normal file
61
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/datachannelstate.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package webrtc
|
||||
|
||||
// DataChannelState indicates the state of a data channel.
|
||||
type DataChannelState int
|
||||
|
||||
const (
|
||||
// DataChannelStateConnecting indicates that the data channel is being
|
||||
// established. This is the initial state of DataChannel, whether created
|
||||
// with CreateDataChannel, or dispatched as a part of an DataChannelEvent.
|
||||
DataChannelStateConnecting DataChannelState = iota + 1
|
||||
|
||||
// DataChannelStateOpen indicates that the underlying data transport is
|
||||
// established and communication is possible.
|
||||
DataChannelStateOpen
|
||||
|
||||
// DataChannelStateClosing indicates that the procedure to close down the
|
||||
// underlying data transport has started.
|
||||
DataChannelStateClosing
|
||||
|
||||
// DataChannelStateClosed indicates that the underlying data transport
|
||||
// has been closed or could not be established.
|
||||
DataChannelStateClosed
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
dataChannelStateConnectingStr = "connecting"
|
||||
dataChannelStateOpenStr = "open"
|
||||
dataChannelStateClosingStr = "closing"
|
||||
dataChannelStateClosedStr = "closed"
|
||||
)
|
||||
|
||||
func newDataChannelState(raw string) DataChannelState {
|
||||
switch raw {
|
||||
case dataChannelStateConnectingStr:
|
||||
return DataChannelStateConnecting
|
||||
case dataChannelStateOpenStr:
|
||||
return DataChannelStateOpen
|
||||
case dataChannelStateClosingStr:
|
||||
return DataChannelStateClosing
|
||||
case dataChannelStateClosedStr:
|
||||
return DataChannelStateClosed
|
||||
default:
|
||||
return DataChannelState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t DataChannelState) String() string {
|
||||
switch t {
|
||||
case DataChannelStateConnecting:
|
||||
return dataChannelStateConnectingStr
|
||||
case DataChannelStateOpen:
|
||||
return dataChannelStateOpenStr
|
||||
case DataChannelStateClosing:
|
||||
return dataChannelStateClosingStr
|
||||
case DataChannelStateClosed:
|
||||
return dataChannelStateClosedStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
14
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go
generated
vendored
Normal file
14
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
package webrtc
|
||||
|
||||
// DTLSFingerprint specifies the hash function algorithm and certificate
|
||||
// fingerprint as described in https://tools.ietf.org/html/rfc4572.
|
||||
type DTLSFingerprint struct {
|
||||
// Algorithm specifies one of the the hash function algorithms defined in
|
||||
// the 'Hash function Textual Names' registry.
|
||||
Algorithm string `json:"algorithm"`
|
||||
|
||||
// Value specifies the value of the certificate fingerprint in lowercase
|
||||
// hex string as expressed utilizing the syntax of 'fingerprint' in
|
||||
// https://tools.ietf.org/html/rfc4572#section-5.
|
||||
Value string `json:"value"`
|
||||
}
|
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsparameters.go
generated
vendored
Normal file
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package webrtc
|
||||
|
||||
// DTLSParameters holds information relating to DTLS configuration.
|
||||
type DTLSParameters struct {
|
||||
Role DTLSRole `json:"role"`
|
||||
Fingerprints []DTLSFingerprint `json:"fingerprints"`
|
||||
}
|
92
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsrole.go
generated
vendored
Normal file
92
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlsrole.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
// DTLSRole indicates the role of the DTLS transport.
|
||||
type DTLSRole byte
|
||||
|
||||
const (
|
||||
// DTLSRoleAuto defines the DTLS role is determined based on
|
||||
// the resolved ICE role: the ICE controlled role acts as the DTLS
|
||||
// client and the ICE controlling role acts as the DTLS server.
|
||||
DTLSRoleAuto DTLSRole = iota + 1
|
||||
|
||||
// DTLSRoleClient defines the DTLS client role.
|
||||
DTLSRoleClient
|
||||
|
||||
// DTLSRoleServer defines the DTLS server role.
|
||||
DTLSRoleServer
|
||||
)
|
||||
|
||||
const (
|
||||
// https://tools.ietf.org/html/rfc5763
|
||||
/*
|
||||
The answerer MUST use either a
|
||||
setup attribute value of setup:active or setup:passive. Note that
|
||||
if the answerer uses setup:passive, then the DTLS handshake will
|
||||
not begin until the answerer is received, which adds additional
|
||||
latency. setup:active allows the answer and the DTLS handshake to
|
||||
occur in parallel. Thus, setup:active is RECOMMENDED.
|
||||
*/
|
||||
defaultDtlsRoleAnswer = DTLSRoleClient
|
||||
/*
|
||||
The endpoint that is the offerer MUST use the setup attribute
|
||||
value of setup:actpass and be prepared to receive a client_hello
|
||||
before it receives the answer.
|
||||
*/
|
||||
defaultDtlsRoleOffer = DTLSRoleAuto
|
||||
)
|
||||
|
||||
func (r DTLSRole) String() string {
|
||||
switch r {
|
||||
case DTLSRoleAuto:
|
||||
return "auto"
|
||||
case DTLSRoleClient:
|
||||
return "client"
|
||||
case DTLSRoleServer:
|
||||
return "server"
|
||||
default:
|
||||
return unknownStr
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate a SessionDescription from a remote to determine if an explicit
|
||||
// role can been determined from it. The decision is made from the first role we we parse.
|
||||
// If no role can be found we return DTLSRoleAuto
|
||||
func dtlsRoleFromRemoteSDP(sessionDescription *sdp.SessionDescription) DTLSRole {
|
||||
if sessionDescription == nil {
|
||||
return DTLSRoleAuto
|
||||
}
|
||||
|
||||
for _, mediaSection := range sessionDescription.MediaDescriptions {
|
||||
for _, attribute := range mediaSection.Attributes {
|
||||
if attribute.Key == "setup" {
|
||||
switch attribute.Value {
|
||||
case sdp.ConnectionRoleActive.String():
|
||||
return DTLSRoleClient
|
||||
case sdp.ConnectionRolePassive.String():
|
||||
return DTLSRoleServer
|
||||
default:
|
||||
return DTLSRoleAuto
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DTLSRoleAuto
|
||||
}
|
||||
|
||||
func connectionRoleFromDtlsRole(d DTLSRole) sdp.ConnectionRole {
|
||||
switch d {
|
||||
case DTLSRoleClient:
|
||||
return sdp.ConnectionRoleActive
|
||||
case DTLSRoleServer:
|
||||
return sdp.ConnectionRolePassive
|
||||
case DTLSRoleAuto:
|
||||
return sdp.ConnectionRoleActpass
|
||||
default:
|
||||
return sdp.ConnectionRole(0)
|
||||
}
|
||||
}
|
419
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlstransport.go
generated
vendored
Normal file
419
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlstransport.go
generated
vendored
Normal file
|
@ -0,0 +1,419 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/dtls/v2"
|
||||
"github.com/pion/dtls/v2/pkg/crypto/fingerprint"
|
||||
"github.com/pion/srtp/v2"
|
||||
"github.com/pion/webrtc/v3/internal/mux"
|
||||
"github.com/pion/webrtc/v3/internal/util"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// DTLSTransport allows an application access to information about the DTLS
|
||||
// transport over which RTP and RTCP packets are sent and received by
|
||||
// RTPSender and RTPReceiver, as well other data such as SCTP packets sent
|
||||
// and received by data channels.
|
||||
type DTLSTransport struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
iceTransport *ICETransport
|
||||
certificates []Certificate
|
||||
remoteParameters DTLSParameters
|
||||
remoteCertificate []byte
|
||||
state DTLSTransportState
|
||||
srtpProtectionProfile srtp.ProtectionProfile
|
||||
|
||||
onStateChangeHandler func(DTLSTransportState)
|
||||
|
||||
conn *dtls.Conn
|
||||
|
||||
srtpSession, srtcpSession atomic.Value
|
||||
srtpEndpoint, srtcpEndpoint *mux.Endpoint
|
||||
simulcastStreams []*srtp.ReadStreamSRTP
|
||||
srtpReady chan struct{}
|
||||
|
||||
dtlsMatcher mux.MatchFunc
|
||||
|
||||
api *API
|
||||
}
|
||||
|
||||
// NewDTLSTransport creates a new DTLSTransport.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certificate) (*DTLSTransport, error) {
|
||||
t := &DTLSTransport{
|
||||
iceTransport: transport,
|
||||
api: api,
|
||||
state: DTLSTransportStateNew,
|
||||
dtlsMatcher: mux.MatchDTLS,
|
||||
srtpReady: make(chan struct{}),
|
||||
}
|
||||
|
||||
if len(certificates) > 0 {
|
||||
now := time.Now()
|
||||
for _, x509Cert := range certificates {
|
||||
if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}
|
||||
}
|
||||
t.certificates = append(t.certificates, x509Cert)
|
||||
}
|
||||
} else {
|
||||
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.UnknownError{Err: err}
|
||||
}
|
||||
certificate, err := GenerateCertificate(sk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.certificates = []Certificate{*certificate}
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// ICETransport returns the currently-configured *ICETransport or nil
|
||||
// if one has not been configured
|
||||
func (t *DTLSTransport) ICETransport() *ICETransport {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
return t.iceTransport
|
||||
}
|
||||
|
||||
// onStateChange requires the caller holds the lock
|
||||
func (t *DTLSTransport) onStateChange(state DTLSTransportState) {
|
||||
t.state = state
|
||||
handler := t.onStateChangeHandler
|
||||
if handler != nil {
|
||||
handler(state)
|
||||
}
|
||||
}
|
||||
|
||||
// OnStateChange sets a handler that is fired when the DTLS
|
||||
// connection state changes.
|
||||
func (t *DTLSTransport) OnStateChange(f func(DTLSTransportState)) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
t.onStateChangeHandler = f
|
||||
}
|
||||
|
||||
// State returns the current dtls transport state.
|
||||
func (t *DTLSTransport) State() DTLSTransportState {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
return t.state
|
||||
}
|
||||
|
||||
// GetLocalParameters returns the DTLS parameters of the local DTLSTransport upon construction.
|
||||
func (t *DTLSTransport) GetLocalParameters() (DTLSParameters, error) {
|
||||
fingerprints := []DTLSFingerprint{}
|
||||
|
||||
for _, c := range t.certificates {
|
||||
prints, err := c.GetFingerprints()
|
||||
if err != nil {
|
||||
return DTLSParameters{}, err
|
||||
}
|
||||
|
||||
fingerprints = append(fingerprints, prints...)
|
||||
}
|
||||
|
||||
return DTLSParameters{
|
||||
Role: DTLSRoleAuto, // always returns the default role
|
||||
Fingerprints: fingerprints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetRemoteCertificate returns the certificate chain in use by the remote side
|
||||
// returns an empty list prior to selection of the remote certificate
|
||||
func (t *DTLSTransport) GetRemoteCertificate() []byte {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
return t.remoteCertificate
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) startSRTP() error {
|
||||
srtpConfig := &srtp.Config{
|
||||
Profile: t.srtpProtectionProfile,
|
||||
BufferFactory: t.api.settingEngine.BufferFactory,
|
||||
LoggerFactory: t.api.settingEngine.LoggerFactory,
|
||||
}
|
||||
if t.api.settingEngine.replayProtection.SRTP != nil {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTPReplayProtection(*t.api.settingEngine.replayProtection.SRTP),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.disableSRTPReplayProtection {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTPNoReplayProtection(),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.replayProtection.SRTCP != nil {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTCPReplayProtection(*t.api.settingEngine.replayProtection.SRTCP),
|
||||
)
|
||||
}
|
||||
|
||||
if t.api.settingEngine.disableSRTCPReplayProtection {
|
||||
srtpConfig.RemoteOptions = append(
|
||||
srtpConfig.RemoteOptions,
|
||||
srtp.SRTCPNoReplayProtection(),
|
||||
)
|
||||
}
|
||||
|
||||
connState := t.conn.ConnectionState()
|
||||
err := srtpConfig.ExtractSessionKeysFromDTLS(&connState, t.role() == DTLSRoleClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", errDtlsKeyExtractionFailed, err)
|
||||
}
|
||||
|
||||
srtpSession, err := srtp.NewSessionSRTP(t.srtpEndpoint, srtpConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", errFailedToStartSRTP, err)
|
||||
}
|
||||
|
||||
srtcpSession, err := srtp.NewSessionSRTCP(t.srtcpEndpoint, srtpConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", errFailedToStartSRTCP, err)
|
||||
}
|
||||
|
||||
t.srtpSession.Store(srtpSession)
|
||||
t.srtcpSession.Store(srtcpSession)
|
||||
close(t.srtpReady)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) getSRTPSession() (*srtp.SessionSRTP, error) {
|
||||
if value := t.srtpSession.Load(); value != nil {
|
||||
return value.(*srtp.SessionSRTP), nil
|
||||
}
|
||||
|
||||
return nil, errDtlsTransportNotStarted
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) {
|
||||
if value := t.srtcpSession.Load(); value != nil {
|
||||
return value.(*srtp.SessionSRTCP), nil
|
||||
}
|
||||
|
||||
return nil, errDtlsTransportNotStarted
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) role() DTLSRole {
|
||||
// If remote has an explicit role use the inverse
|
||||
switch t.remoteParameters.Role {
|
||||
case DTLSRoleClient:
|
||||
return DTLSRoleServer
|
||||
case DTLSRoleServer:
|
||||
return DTLSRoleClient
|
||||
default:
|
||||
}
|
||||
|
||||
// If SettingEngine has an explicit role
|
||||
switch t.api.settingEngine.answeringDTLSRole {
|
||||
case DTLSRoleServer:
|
||||
return DTLSRoleServer
|
||||
case DTLSRoleClient:
|
||||
return DTLSRoleClient
|
||||
default:
|
||||
}
|
||||
|
||||
// Remote was auto and no explicit role was configured via SettingEngine
|
||||
if t.iceTransport.Role() == ICERoleControlling {
|
||||
return DTLSRoleServer
|
||||
}
|
||||
return defaultDtlsRoleAnswer
|
||||
}
|
||||
|
||||
// Start DTLS transport negotiation with the parameters of the remote DTLS transport
|
||||
func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error {
|
||||
// Take lock and prepare connection, we must not hold the lock
|
||||
// when connecting
|
||||
prepareTransport := func() (DTLSRole, *dtls.Config, error) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if err := t.ensureICEConn(); err != nil {
|
||||
return DTLSRole(0), nil, err
|
||||
}
|
||||
|
||||
if t.state != DTLSTransportStateNew {
|
||||
return DTLSRole(0), nil, &rtcerr.InvalidStateError{Err: fmt.Errorf("%w: %s", errInvalidDTLSStart, t.state)}
|
||||
}
|
||||
|
||||
t.srtpEndpoint = t.iceTransport.NewEndpoint(mux.MatchSRTP)
|
||||
t.srtcpEndpoint = t.iceTransport.NewEndpoint(mux.MatchSRTCP)
|
||||
t.remoteParameters = remoteParameters
|
||||
|
||||
cert := t.certificates[0]
|
||||
t.onStateChange(DTLSTransportStateConnecting)
|
||||
|
||||
return t.role(), &dtls.Config{
|
||||
Certificates: []tls.Certificate{
|
||||
{
|
||||
Certificate: [][]byte{cert.x509Cert.Raw},
|
||||
PrivateKey: cert.privateKey,
|
||||
},
|
||||
},
|
||||
SRTPProtectionProfiles: []dtls.SRTPProtectionProfile{dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80},
|
||||
ClientAuth: dtls.RequireAnyClientCert,
|
||||
LoggerFactory: t.api.settingEngine.LoggerFactory,
|
||||
InsecureSkipVerify: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var dtlsConn *dtls.Conn
|
||||
dtlsEndpoint := t.iceTransport.NewEndpoint(mux.MatchDTLS)
|
||||
role, dtlsConfig, err := prepareTransport()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.api.settingEngine.replayProtection.DTLS != nil {
|
||||
dtlsConfig.ReplayProtectionWindow = int(*t.api.settingEngine.replayProtection.DTLS)
|
||||
}
|
||||
|
||||
// Connect as DTLS Client/Server, function is blocking and we
|
||||
// must not hold the DTLSTransport lock
|
||||
if role == DTLSRoleClient {
|
||||
dtlsConn, err = dtls.Client(dtlsEndpoint, dtlsConfig)
|
||||
} else {
|
||||
dtlsConn, err = dtls.Server(dtlsEndpoint, dtlsConfig)
|
||||
}
|
||||
|
||||
// Re-take the lock, nothing beyond here is blocking
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if err != nil {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return err
|
||||
}
|
||||
|
||||
srtpProfile, ok := dtlsConn.SelectedSRTPProtectionProfile()
|
||||
if !ok {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return ErrNoSRTPProtectionProfile
|
||||
}
|
||||
|
||||
switch srtpProfile {
|
||||
case dtls.SRTP_AEAD_AES_128_GCM:
|
||||
t.srtpProtectionProfile = srtp.ProtectionProfileAeadAes128Gcm
|
||||
case dtls.SRTP_AES128_CM_HMAC_SHA1_80:
|
||||
t.srtpProtectionProfile = srtp.ProtectionProfileAes128CmHmacSha1_80
|
||||
default:
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return ErrNoSRTPProtectionProfile
|
||||
}
|
||||
|
||||
t.conn = dtlsConn
|
||||
t.onStateChange(DTLSTransportStateConnected)
|
||||
|
||||
if t.api.settingEngine.disableCertificateFingerprintVerification {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check the fingerprint if a certificate was exchanged
|
||||
remoteCerts := t.conn.ConnectionState().PeerCertificates
|
||||
if len(remoteCerts) == 0 {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return errNoRemoteCertificate
|
||||
}
|
||||
t.remoteCertificate = remoteCerts[0]
|
||||
|
||||
parsedRemoteCert, err := x509.ParseCertificate(t.remoteCertificate)
|
||||
if err != nil {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.validateFingerPrint(parsedRemoteCert); err != nil {
|
||||
t.onStateChange(DTLSTransportStateFailed)
|
||||
return err
|
||||
}
|
||||
|
||||
return t.startSRTP()
|
||||
}
|
||||
|
||||
// Stop stops and closes the DTLSTransport object.
|
||||
func (t *DTLSTransport) Stop() error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
// Try closing everything and collect the errors
|
||||
var closeErrs []error
|
||||
|
||||
if srtpSessionValue := t.srtpSession.Load(); srtpSessionValue != nil {
|
||||
closeErrs = append(closeErrs, srtpSessionValue.(*srtp.SessionSRTP).Close())
|
||||
}
|
||||
|
||||
if srtcpSessionValue := t.srtcpSession.Load(); srtcpSessionValue != nil {
|
||||
closeErrs = append(closeErrs, srtcpSessionValue.(*srtp.SessionSRTCP).Close())
|
||||
}
|
||||
|
||||
for i := range t.simulcastStreams {
|
||||
closeErrs = append(closeErrs, t.simulcastStreams[i].Close())
|
||||
}
|
||||
|
||||
if t.conn != nil {
|
||||
// dtls connection may be closed on sctp close.
|
||||
if err := t.conn.Close(); err != nil && !errors.Is(err, dtls.ErrConnClosed) {
|
||||
closeErrs = append(closeErrs, err)
|
||||
}
|
||||
}
|
||||
t.onStateChange(DTLSTransportStateClosed)
|
||||
return util.FlattenErrs(closeErrs)
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) validateFingerPrint(remoteCert *x509.Certificate) error {
|
||||
for _, fp := range t.remoteParameters.Fingerprints {
|
||||
hashAlgo, err := fingerprint.HashFromString(fp.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteValue, err := fingerprint.Fingerprint(remoteCert, hashAlgo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.EqualFold(remoteValue, fp.Value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errNoMatchingCertificateFingerprint
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) ensureICEConn() error {
|
||||
if t.iceTransport == nil || t.iceTransport.State() == ICETransportStateNew {
|
||||
return errICEConnectionNotStarted
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DTLSTransport) storeSimulcastStream(s *srtp.ReadStreamSRTP) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
t.simulcastStreams = append(t.simulcastStreams, s)
|
||||
}
|
71
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go
generated
vendored
Normal file
71
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
package webrtc
|
||||
|
||||
// DTLSTransportState indicates the DTLS transport establishment state.
|
||||
type DTLSTransportState int
|
||||
|
||||
const (
|
||||
// DTLSTransportStateNew indicates that DTLS has not started negotiating
|
||||
// yet.
|
||||
DTLSTransportStateNew DTLSTransportState = iota + 1
|
||||
|
||||
// DTLSTransportStateConnecting indicates that DTLS is in the process of
|
||||
// negotiating a secure connection and verifying the remote fingerprint.
|
||||
DTLSTransportStateConnecting
|
||||
|
||||
// DTLSTransportStateConnected indicates that DTLS has completed
|
||||
// negotiation of a secure connection and verified the remote fingerprint.
|
||||
DTLSTransportStateConnected
|
||||
|
||||
// DTLSTransportStateClosed indicates that the transport has been closed
|
||||
// intentionally as the result of receipt of a close_notify alert, or
|
||||
// calling close().
|
||||
DTLSTransportStateClosed
|
||||
|
||||
// DTLSTransportStateFailed indicates that the transport has failed as
|
||||
// the result of an error (such as receipt of an error alert or failure to
|
||||
// validate the remote fingerprint).
|
||||
DTLSTransportStateFailed
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
dtlsTransportStateNewStr = "new"
|
||||
dtlsTransportStateConnectingStr = "connecting"
|
||||
dtlsTransportStateConnectedStr = "connected"
|
||||
dtlsTransportStateClosedStr = "closed"
|
||||
dtlsTransportStateFailedStr = "failed"
|
||||
)
|
||||
|
||||
func newDTLSTransportState(raw string) DTLSTransportState {
|
||||
switch raw {
|
||||
case dtlsTransportStateNewStr:
|
||||
return DTLSTransportStateNew
|
||||
case dtlsTransportStateConnectingStr:
|
||||
return DTLSTransportStateConnecting
|
||||
case dtlsTransportStateConnectedStr:
|
||||
return DTLSTransportStateConnected
|
||||
case dtlsTransportStateClosedStr:
|
||||
return DTLSTransportStateClosed
|
||||
case dtlsTransportStateFailedStr:
|
||||
return DTLSTransportStateFailed
|
||||
default:
|
||||
return DTLSTransportState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t DTLSTransportState) String() string {
|
||||
switch t {
|
||||
case DTLSTransportStateNew:
|
||||
return dtlsTransportStateNewStr
|
||||
case DTLSTransportStateConnecting:
|
||||
return dtlsTransportStateConnectingStr
|
||||
case DTLSTransportStateConnected:
|
||||
return dtlsTransportStateConnectedStr
|
||||
case DTLSTransportStateClosed:
|
||||
return dtlsTransportStateClosedStr
|
||||
case DTLSTransportStateFailed:
|
||||
return dtlsTransportStateFailedStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
220
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/errors.go
generated
vendored
Normal file
220
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,220 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUnknownType indicates an error with Unknown info.
|
||||
ErrUnknownType = errors.New("unknown")
|
||||
|
||||
// ErrConnectionClosed indicates an operation executed after connection
|
||||
// has already been closed.
|
||||
ErrConnectionClosed = errors.New("connection closed")
|
||||
|
||||
// ErrDataChannelNotOpen indicates an operation executed when the data
|
||||
// channel is not (yet) open.
|
||||
ErrDataChannelNotOpen = errors.New("data channel not open")
|
||||
|
||||
// ErrCertificateExpired indicates that an x509 certificate has expired.
|
||||
ErrCertificateExpired = errors.New("x509Cert expired")
|
||||
|
||||
// ErrNoTurnCredentials indicates that a TURN server URL was provided
|
||||
// without required credentials.
|
||||
ErrNoTurnCredentials = errors.New("turn server credentials required")
|
||||
|
||||
// ErrTurnCredentials indicates that provided TURN credentials are partial
|
||||
// or malformed.
|
||||
ErrTurnCredentials = errors.New("invalid turn server credentials")
|
||||
|
||||
// ErrExistingTrack indicates that a track already exists.
|
||||
ErrExistingTrack = errors.New("track already exists")
|
||||
|
||||
// ErrPrivateKeyType indicates that a particular private key encryption
|
||||
// chosen to generate a certificate is not supported.
|
||||
ErrPrivateKeyType = errors.New("private key type not supported")
|
||||
|
||||
// ErrModifyingPeerIdentity indicates that an attempt to modify
|
||||
// PeerIdentity was made after PeerConnection has been initialized.
|
||||
ErrModifyingPeerIdentity = errors.New("peerIdentity cannot be modified")
|
||||
|
||||
// ErrModifyingCertificates indicates that an attempt to modify
|
||||
// Certificates was made after PeerConnection has been initialized.
|
||||
ErrModifyingCertificates = errors.New("certificates cannot be modified")
|
||||
|
||||
// ErrModifyingBundlePolicy indicates that an attempt to modify
|
||||
// BundlePolicy was made after PeerConnection has been initialized.
|
||||
ErrModifyingBundlePolicy = errors.New("bundle policy cannot be modified")
|
||||
|
||||
// ErrModifyingRTCPMuxPolicy indicates that an attempt to modify
|
||||
// RTCPMuxPolicy was made after PeerConnection has been initialized.
|
||||
ErrModifyingRTCPMuxPolicy = errors.New("rtcp mux policy cannot be modified")
|
||||
|
||||
// ErrModifyingICECandidatePoolSize indicates that an attempt to modify
|
||||
// ICECandidatePoolSize was made after PeerConnection has been initialized.
|
||||
ErrModifyingICECandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
|
||||
|
||||
// ErrStringSizeLimit indicates that the character size limit of string is
|
||||
// exceeded. The limit is hardcoded to 65535 according to specifications.
|
||||
ErrStringSizeLimit = errors.New("data channel label exceeds size limit")
|
||||
|
||||
// ErrMaxDataChannelID indicates that the maximum number ID that could be
|
||||
// specified for a data channel has been exceeded.
|
||||
ErrMaxDataChannelID = errors.New("maximum number ID for datachannel specified")
|
||||
|
||||
// ErrNegotiatedWithoutID indicates that an attempt to create a data channel
|
||||
// was made while setting the negotiated option to true without providing
|
||||
// the negotiated channel ID.
|
||||
ErrNegotiatedWithoutID = errors.New("negotiated set without channel id")
|
||||
|
||||
// ErrRetransmitsOrPacketLifeTime indicates that an attempt to create a data
|
||||
// channel was made with both options MaxPacketLifeTime and MaxRetransmits
|
||||
// set together. Such configuration is not supported by the specification
|
||||
// and is mutually exclusive.
|
||||
ErrRetransmitsOrPacketLifeTime = errors.New("both MaxPacketLifeTime and MaxRetransmits was set")
|
||||
|
||||
// ErrCodecNotFound is returned when a codec search to the Media Engine fails
|
||||
ErrCodecNotFound = errors.New("codec not found")
|
||||
|
||||
// ErrNoRemoteDescription indicates that an operation was rejected because
|
||||
// the remote description is not set
|
||||
ErrNoRemoteDescription = errors.New("remote description is not set")
|
||||
|
||||
// ErrIncorrectSDPSemantics indicates that the PeerConnection was configured to
|
||||
// generate SDP Answers with different SDP Semantics than the received Offer
|
||||
ErrIncorrectSDPSemantics = errors.New("offer SDP semantics does not match configuration")
|
||||
|
||||
// ErrIncorrectSignalingState indicates that the signaling state of PeerConnection is not correct
|
||||
ErrIncorrectSignalingState = errors.New("operation can not be run in current signaling state")
|
||||
|
||||
// ErrProtocolTooLarge indicates that value given for a DataChannelInit protocol is
|
||||
// longer then 65535 bytes
|
||||
ErrProtocolTooLarge = errors.New("protocol is larger then 65535 bytes")
|
||||
|
||||
// ErrSenderNotCreatedByConnection indicates RemoveTrack was called with a RtpSender not created
|
||||
// by this PeerConnection
|
||||
ErrSenderNotCreatedByConnection = errors.New("RtpSender not created by this PeerConnection")
|
||||
|
||||
// ErrSessionDescriptionNoFingerprint indicates SetRemoteDescription was called with a SessionDescription that has no
|
||||
// fingerprint
|
||||
ErrSessionDescriptionNoFingerprint = errors.New("SetRemoteDescription called with no fingerprint")
|
||||
|
||||
// ErrSessionDescriptionInvalidFingerprint indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// has an invalid fingerprint
|
||||
ErrSessionDescriptionInvalidFingerprint = errors.New("SetRemoteDescription called with an invalid fingerprint")
|
||||
|
||||
// ErrSessionDescriptionConflictingFingerprints indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// has an conflicting fingerprints
|
||||
ErrSessionDescriptionConflictingFingerprints = errors.New("SetRemoteDescription called with multiple conflicting fingerprint")
|
||||
|
||||
// ErrSessionDescriptionMissingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// is missing an ice-ufrag value
|
||||
ErrSessionDescriptionMissingIceUfrag = errors.New("SetRemoteDescription called with no ice-ufrag")
|
||||
|
||||
// ErrSessionDescriptionMissingIcePwd indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// is missing an ice-pwd value
|
||||
ErrSessionDescriptionMissingIcePwd = errors.New("SetRemoteDescription called with no ice-pwd")
|
||||
|
||||
// ErrSessionDescriptionConflictingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// contains multiple conflicting ice-ufrag values
|
||||
ErrSessionDescriptionConflictingIceUfrag = errors.New("SetRemoteDescription called with multiple conflicting ice-ufrag values")
|
||||
|
||||
// ErrSessionDescriptionConflictingIcePwd indicates SetRemoteDescription was called with a SessionDescription that
|
||||
// contains multiple conflicting ice-pwd values
|
||||
ErrSessionDescriptionConflictingIcePwd = errors.New("SetRemoteDescription called with multiple conflicting ice-pwd values")
|
||||
|
||||
// ErrNoSRTPProtectionProfile indicates that the DTLS handshake completed and no SRTP Protection Profile was chosen
|
||||
ErrNoSRTPProtectionProfile = errors.New("DTLS Handshake completed and no SRTP Protection Profile was chosen")
|
||||
|
||||
// ErrFailedToGenerateCertificateFingerprint indicates that we failed to generate the fingerprint used for comparing certificates
|
||||
ErrFailedToGenerateCertificateFingerprint = errors.New("failed to generate certificate fingerprint")
|
||||
|
||||
// ErrNoCodecsAvailable indicates that operation isn't possible because the MediaEngine has no codecs available
|
||||
ErrNoCodecsAvailable = errors.New("operation failed no codecs are available")
|
||||
|
||||
// ErrUnsupportedCodec indicates the remote peer doesn't support the requested codec
|
||||
ErrUnsupportedCodec = errors.New("unable to start track, codec is not supported by remote")
|
||||
|
||||
// ErrUnbindFailed indicates that a TrackLocal was not able to be unbind
|
||||
ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection")
|
||||
|
||||
// ErrNoPayloaderForCodec indicates that the requested codec does not have a payloader
|
||||
ErrNoPayloaderForCodec = errors.New("the requested codec does not have a payloader")
|
||||
|
||||
// ErrRegisterHeaderExtensionInvalidDirection indicates that a extension was registered with a direction besides `sendonly` or `recvonly`
|
||||
ErrRegisterHeaderExtensionInvalidDirection = errors.New("a header extension must be registered as 'recvonly', 'sendonly' or both")
|
||||
|
||||
// ErrSimulcastProbeOverflow indicates that too many Simulcast probe streams are in flight and the requested SSRC was ignored
|
||||
ErrSimulcastProbeOverflow = errors.New("simulcast probe limit has been reached, new SSRC has been discarded")
|
||||
|
||||
errDetachNotEnabled = errors.New("enable detaching by calling webrtc.DetachDataChannels()")
|
||||
errDetachBeforeOpened = errors.New("datachannel not opened yet, try calling Detach from OnOpen")
|
||||
errDtlsTransportNotStarted = errors.New("the DTLS transport has not started yet")
|
||||
errDtlsKeyExtractionFailed = errors.New("failed extracting keys from DTLS for SRTP")
|
||||
errFailedToStartSRTP = errors.New("failed to start SRTP")
|
||||
errFailedToStartSRTCP = errors.New("failed to start SRTCP")
|
||||
errInvalidDTLSStart = errors.New("attempted to start DTLSTransport that is not in new state")
|
||||
errNoRemoteCertificate = errors.New("peer didn't provide certificate via DTLS")
|
||||
errIdentityProviderNotImplemented = errors.New("identity provider is not implemented")
|
||||
errNoMatchingCertificateFingerprint = errors.New("remote certificate does not match any fingerprint")
|
||||
|
||||
errICEConnectionNotStarted = errors.New("ICE connection not started")
|
||||
errICECandidateTypeUnknown = errors.New("unknown candidate type")
|
||||
errICEInvalidConvertCandidateType = errors.New("cannot convert ice.CandidateType into webrtc.ICECandidateType, invalid type")
|
||||
errICEAgentNotExist = errors.New("ICEAgent does not exist")
|
||||
errICECandiatesCoversionFailed = errors.New("unable to convert ICE candidates to ICECandidates")
|
||||
errICERoleUnknown = errors.New("unknown ICE Role")
|
||||
errICEProtocolUnknown = errors.New("unknown protocol")
|
||||
errICEGathererNotStarted = errors.New("gatherer not started")
|
||||
|
||||
errNetworkTypeUnknown = errors.New("unknown network type")
|
||||
|
||||
errSDPDoesNotMatchOffer = errors.New("new sdp does not match previous offer")
|
||||
errSDPDoesNotMatchAnswer = errors.New("new sdp does not match previous answer")
|
||||
errPeerConnSDPTypeInvalidValue = errors.New("provided value is not a valid enum value of type SDPType")
|
||||
errPeerConnStateChangeInvalid = errors.New("invalid state change op")
|
||||
errPeerConnStateChangeUnhandled = errors.New("unhandled state change op")
|
||||
errPeerConnSDPTypeInvalidValueSetLocalDescription = errors.New("invalid SDP type supplied to SetLocalDescription()")
|
||||
errPeerConnRemoteDescriptionWithoutMidValue = errors.New("remoteDescription contained media section without mid value")
|
||||
errPeerConnRemoteDescriptionNil = errors.New("remoteDescription has not been set yet")
|
||||
errPeerConnSingleMediaSectionHasExplicitSSRC = errors.New("single media section has an explicit SSRC")
|
||||
errPeerConnRemoteSSRCAddTransceiver = errors.New("could not add transceiver for remote SSRC")
|
||||
errPeerConnSimulcastMidRTPExtensionRequired = errors.New("mid RTP Extensions required for Simulcast")
|
||||
errPeerConnSimulcastStreamIDRTPExtensionRequired = errors.New("stream id RTP Extensions required for Simulcast")
|
||||
errPeerConnSimulcastIncomingSSRCFailed = errors.New("incoming SSRC failed Simulcast probing")
|
||||
errPeerConnAddTransceiverFromKindOnlyAcceptsOne = errors.New("AddTransceiverFromKind only accepts one RtpTransceiverInit")
|
||||
errPeerConnAddTransceiverFromTrackOnlyAcceptsOne = errors.New("AddTransceiverFromTrack only accepts one RtpTransceiverInit")
|
||||
errPeerConnAddTransceiverFromKindSupport = errors.New("AddTransceiverFromKind currently only supports recvonly")
|
||||
errPeerConnAddTransceiverFromTrackSupport = errors.New("AddTransceiverFromTrack currently only supports sendonly and sendrecv")
|
||||
errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider")
|
||||
errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream")
|
||||
errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid")
|
||||
|
||||
errRTPReceiverDTLSTransportNil = errors.New("DTLSTransport must not be nil")
|
||||
errRTPReceiverReceiveAlreadyCalled = errors.New("Receive has already been called")
|
||||
errRTPReceiverWithSSRCTrackStreamNotFound = errors.New("unable to find stream for Track with SSRC")
|
||||
errRTPReceiverForSSRCTrackStreamNotFound = errors.New("no trackStreams found for SSRC")
|
||||
errRTPReceiverForRIDTrackStreamNotFound = errors.New("no trackStreams found for RID")
|
||||
|
||||
errRTPSenderTrackNil = errors.New("Track must not be nil")
|
||||
errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil")
|
||||
errRTPSenderSendAlreadyCalled = errors.New("Send has already been called")
|
||||
|
||||
errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil")
|
||||
errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending")
|
||||
|
||||
errSCTPTransportDTLS = errors.New("DTLS not established")
|
||||
|
||||
errSDPZeroTransceivers = errors.New("addTransceiverSDP() called with 0 transceivers")
|
||||
errSDPMediaSectionMediaDataChanInvalid = errors.New("invalid Media Section. Media + DataChannel both enabled")
|
||||
errSDPMediaSectionMultipleTrackInvalid = errors.New("invalid Media Section. Can not have multiple tracks in one MediaSection in UnifiedPlan")
|
||||
|
||||
errSettingEngineSetAnsweringDTLSRole = errors.New("SetAnsweringDTLSRole must DTLSRoleClient or DTLSRoleServer")
|
||||
|
||||
errSignalingStateCannotRollback = errors.New("can't rollback from stable state")
|
||||
errSignalingStateProposedTransitionInvalid = errors.New("invalid proposed signaling state transition")
|
||||
|
||||
errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state")
|
||||
|
||||
errICETransportNotInNew = errors.New("ICETransport can only be called in ICETransportStateNew")
|
||||
)
|
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go
generated
vendored
Normal file
24
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// GatheringCompletePromise is a Pion specific helper function that returns a channel that is closed when gathering is complete.
|
||||
// This function may be helpful in cases where you are unable to trickle your ICE Candidates.
|
||||
//
|
||||
// It is better to not use this function, and instead trickle candidates. If you use this function you will see longer connection startup times.
|
||||
// When the call is connected you will see no impact however.
|
||||
func GatheringCompletePromise(pc *PeerConnection) (gatherComplete <-chan struct{}) {
|
||||
gatheringComplete, done := context.WithCancel(context.Background())
|
||||
|
||||
// It's possible to miss the GatherComplete event since setGatherCompleteHandler is an atomic operation and the
|
||||
// promise might have been created after the gathering is finished. Therefore, we need to check if the ICE gathering
|
||||
// state has changed to complete so that we don't block the caller forever.
|
||||
pc.setGatherCompleteHandler(func() { done() })
|
||||
if pc.ICEGatheringState() == ICEGatheringStateComplete {
|
||||
done()
|
||||
}
|
||||
|
||||
return gatheringComplete.Done()
|
||||
}
|
23
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/go.mod
generated
vendored
Normal file
23
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
module github.com/pion/webrtc/v3
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/onsi/ginkgo v1.14.2 // indirect
|
||||
github.com/onsi/gomega v1.10.3 // indirect
|
||||
github.com/pion/datachannel v1.4.21
|
||||
github.com/pion/dtls/v2 v2.0.4
|
||||
github.com/pion/ice/v2 v2.0.14
|
||||
github.com/pion/interceptor v0.0.9
|
||||
github.com/pion/logging v0.2.2
|
||||
github.com/pion/randutil v0.1.0
|
||||
github.com/pion/rtcp v1.2.6
|
||||
github.com/pion/rtp v1.6.2
|
||||
github.com/pion/sctp v1.7.11
|
||||
github.com/pion/sdp/v3 v3.0.4
|
||||
github.com/pion/srtp/v2 v2.0.1
|
||||
github.com/pion/transport v0.12.2
|
||||
github.com/sclevine/agouti v3.0.0+incompatible
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7
|
||||
)
|
138
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/go.sum
generated
vendored
Normal file
138
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
|
||||
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
|
||||
github.com/pion/ice/v2 v2.0.14 h1:FxXxauyykf89SWAtkQCfnHkno6G8+bhRkNguSh9zU+4=
|
||||
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM=
|
||||
github.com/pion/interceptor v0.0.9 h1:fk5hTdyLO3KURQsf/+RjMpEm4NE3yeTY9Kh97b5BvwA=
|
||||
github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
||||
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
||||
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/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
|
||||
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
||||
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
|
||||
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE=
|
||||
github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8=
|
||||
github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||
github.com/pion/srtp/v2 v2.0.1 h1:kgfh65ob3EcnFYA4kUBvU/menCp9u7qaJLXwWgpobzs=
|
||||
github.com/pion/srtp/v2 v2.0.1/go.mod h1:c8NWHhhkFf/drmHTAblkdu8++lsISEBBdAuiyxgqIsE=
|
||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
||||
github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
|
||||
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
|
||||
github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
|
||||
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
|
||||
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
||||
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
|
||||
golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/ice_go.go
generated
vendored
Normal file
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/ice_go.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
// NewICETransport creates a new NewICETransport.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewICETransport(gatherer *ICEGatherer) *ICETransport {
|
||||
return NewICETransport(gatherer, api.settingEngine.LoggerFactory)
|
||||
}
|
169
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidate.go
generated
vendored
Normal file
169
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidate.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
)
|
||||
|
||||
// ICECandidate represents a ice candidate
|
||||
type ICECandidate struct {
|
||||
statsID string
|
||||
Foundation string `json:"foundation"`
|
||||
Priority uint32 `json:"priority"`
|
||||
Address string `json:"address"`
|
||||
Protocol ICEProtocol `json:"protocol"`
|
||||
Port uint16 `json:"port"`
|
||||
Typ ICECandidateType `json:"type"`
|
||||
Component uint16 `json:"component"`
|
||||
RelatedAddress string `json:"relatedAddress"`
|
||||
RelatedPort uint16 `json:"relatedPort"`
|
||||
TCPType string `json:"tcpType"`
|
||||
}
|
||||
|
||||
// Conversion for package ice
|
||||
|
||||
func newICECandidatesFromICE(iceCandidates []ice.Candidate) ([]ICECandidate, error) {
|
||||
candidates := []ICECandidate{}
|
||||
|
||||
for _, i := range iceCandidates {
|
||||
c, err := newICECandidateFromICE(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidates = append(candidates, c)
|
||||
}
|
||||
|
||||
return candidates, nil
|
||||
}
|
||||
|
||||
func newICECandidateFromICE(i ice.Candidate) (ICECandidate, error) {
|
||||
typ, err := convertTypeFromICE(i.Type())
|
||||
if err != nil {
|
||||
return ICECandidate{}, err
|
||||
}
|
||||
protocol, err := NewICEProtocol(i.NetworkType().NetworkShort())
|
||||
if err != nil {
|
||||
return ICECandidate{}, err
|
||||
}
|
||||
|
||||
c := ICECandidate{
|
||||
statsID: i.ID(),
|
||||
Foundation: i.Foundation(),
|
||||
Priority: i.Priority(),
|
||||
Address: i.Address(),
|
||||
Protocol: protocol,
|
||||
Port: uint16(i.Port()),
|
||||
Component: i.Component(),
|
||||
Typ: typ,
|
||||
TCPType: i.TCPType().String(),
|
||||
}
|
||||
|
||||
if i.RelatedAddress() != nil {
|
||||
c.RelatedAddress = i.RelatedAddress().Address
|
||||
c.RelatedPort = uint16(i.RelatedAddress().Port)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c ICECandidate) toICE() (ice.Candidate, error) {
|
||||
candidateID := c.statsID
|
||||
switch c.Typ {
|
||||
case ICECandidateTypeHost:
|
||||
config := ice.CandidateHostConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
TCPType: ice.NewTCPType(c.TCPType),
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
}
|
||||
return ice.NewCandidateHost(&config)
|
||||
case ICECandidateTypeSrflx:
|
||||
config := ice.CandidateServerReflexiveConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
return ice.NewCandidateServerReflexive(&config)
|
||||
case ICECandidateTypePrflx:
|
||||
config := ice.CandidatePeerReflexiveConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
return ice.NewCandidatePeerReflexive(&config)
|
||||
case ICECandidateTypeRelay:
|
||||
config := ice.CandidateRelayConfig{
|
||||
CandidateID: candidateID,
|
||||
Network: c.Protocol.String(),
|
||||
Address: c.Address,
|
||||
Port: int(c.Port),
|
||||
Component: c.Component,
|
||||
Foundation: c.Foundation,
|
||||
Priority: c.Priority,
|
||||
RelAddr: c.RelatedAddress,
|
||||
RelPort: int(c.RelatedPort),
|
||||
}
|
||||
return ice.NewCandidateRelay(&config)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ)
|
||||
}
|
||||
}
|
||||
|
||||
func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
|
||||
switch t {
|
||||
case ice.CandidateTypeHost:
|
||||
return ICECandidateTypeHost, nil
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
return ICECandidateTypeSrflx, nil
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
return ICECandidateTypePrflx, nil
|
||||
case ice.CandidateTypeRelay:
|
||||
return ICECandidateTypeRelay, nil
|
||||
default:
|
||||
return ICECandidateType(t), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, t)
|
||||
}
|
||||
}
|
||||
|
||||
func (c ICECandidate) String() string {
|
||||
ic, err := c.toICE()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err)
|
||||
}
|
||||
return ic.String()
|
||||
}
|
||||
|
||||
// ToJSON returns an ICECandidateInit
|
||||
// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
|
||||
func (c ICECandidate) ToJSON() ICECandidateInit {
|
||||
zeroVal := uint16(0)
|
||||
emptyStr := ""
|
||||
candidateStr := ""
|
||||
|
||||
candidate, err := c.toICE()
|
||||
if err == nil {
|
||||
candidateStr = candidate.Marshal()
|
||||
}
|
||||
|
||||
return ICECandidateInit{
|
||||
Candidate: fmt.Sprintf("candidate:%s", candidateStr),
|
||||
SDPMid: &emptyStr,
|
||||
SDPMLineIndex: &zeroVal,
|
||||
}
|
||||
}
|
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidateinit.go
generated
vendored
Normal file
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidateinit.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package webrtc
|
||||
|
||||
// ICECandidateInit is used to serialize ice candidates
|
||||
type ICECandidateInit struct {
|
||||
Candidate string `json:"candidate"`
|
||||
SDPMid *string `json:"sdpMid"`
|
||||
SDPMLineIndex *uint16 `json:"sdpMLineIndex"`
|
||||
UsernameFragment *string `json:"usernameFragment"`
|
||||
}
|
29
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidatepair.go
generated
vendored
Normal file
29
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidatepair.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package webrtc
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ICECandidatePair represents an ICE Candidate pair
|
||||
type ICECandidatePair struct {
|
||||
statsID string
|
||||
Local *ICECandidate
|
||||
Remote *ICECandidate
|
||||
}
|
||||
|
||||
func newICECandidatePairStatsID(localID, remoteID string) string {
|
||||
return fmt.Sprintf("%s-%s", localID, remoteID)
|
||||
}
|
||||
|
||||
func (p *ICECandidatePair) String() string {
|
||||
return fmt.Sprintf("(local) %s <-> (remote) %s", p.Local, p.Remote)
|
||||
}
|
||||
|
||||
// NewICECandidatePair returns an initialized *ICECandidatePair
|
||||
// for the given pair of ICECandidate instances
|
||||
func NewICECandidatePair(local, remote *ICECandidate) *ICECandidatePair {
|
||||
statsID := newICECandidatePairStatsID(local.statsID, remote.statsID)
|
||||
return &ICECandidatePair{
|
||||
statsID: statsID,
|
||||
Local: local,
|
||||
Remote: remote,
|
||||
}
|
||||
}
|
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidatetype.go
generated
vendored
Normal file
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecandidatetype.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
)
|
||||
|
||||
// ICECandidateType represents the type of the ICE candidate used.
|
||||
type ICECandidateType int
|
||||
|
||||
const (
|
||||
// ICECandidateTypeHost indicates that the candidate is of Host type as
|
||||
// described in https://tools.ietf.org/html/rfc8445#section-5.1.1.1. A
|
||||
// candidate obtained by binding to a specific port from an IP address on
|
||||
// the host. This includes IP addresses on physical interfaces and logical
|
||||
// ones, such as ones obtained through VPNs.
|
||||
ICECandidateTypeHost ICECandidateType = iota + 1
|
||||
|
||||
// ICECandidateTypeSrflx indicates the the candidate is of Server
|
||||
// Reflexive type as described
|
||||
// https://tools.ietf.org/html/rfc8445#section-5.1.1.2. A candidate type
|
||||
// whose IP address and port are a binding allocated by a NAT for an ICE
|
||||
// agent after it sends a packet through the NAT to a server, such as a
|
||||
// STUN server.
|
||||
ICECandidateTypeSrflx
|
||||
|
||||
// ICECandidateTypePrflx indicates that the candidate is of Peer
|
||||
// Reflexive type. A candidate type whose IP address and port are a binding
|
||||
// allocated by a NAT for an ICE agent after it sends a packet through the
|
||||
// NAT to its peer.
|
||||
ICECandidateTypePrflx
|
||||
|
||||
// ICECandidateTypeRelay indicates the the candidate is of Relay type as
|
||||
// described in https://tools.ietf.org/html/rfc8445#section-5.1.1.2. A
|
||||
// candidate type obtained from a relay server, such as a TURN server.
|
||||
ICECandidateTypeRelay
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceCandidateTypeHostStr = "host"
|
||||
iceCandidateTypeSrflxStr = "srflx"
|
||||
iceCandidateTypePrflxStr = "prflx"
|
||||
iceCandidateTypeRelayStr = "relay"
|
||||
)
|
||||
|
||||
// NewICECandidateType takes a string and converts it into ICECandidateType
|
||||
func NewICECandidateType(raw string) (ICECandidateType, error) {
|
||||
switch raw {
|
||||
case iceCandidateTypeHostStr:
|
||||
return ICECandidateTypeHost, nil
|
||||
case iceCandidateTypeSrflxStr:
|
||||
return ICECandidateTypeSrflx, nil
|
||||
case iceCandidateTypePrflxStr:
|
||||
return ICECandidateTypePrflx, nil
|
||||
case iceCandidateTypeRelayStr:
|
||||
return ICECandidateTypeRelay, nil
|
||||
default:
|
||||
return ICECandidateType(Unknown), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, raw)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICECandidateType) String() string {
|
||||
switch t {
|
||||
case ICECandidateTypeHost:
|
||||
return iceCandidateTypeHostStr
|
||||
case ICECandidateTypeSrflx:
|
||||
return iceCandidateTypeSrflxStr
|
||||
case ICECandidateTypePrflx:
|
||||
return iceCandidateTypePrflxStr
|
||||
case ICECandidateTypeRelay:
|
||||
return iceCandidateTypeRelayStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
func getCandidateType(candidateType ice.CandidateType) (ICECandidateType, error) {
|
||||
switch candidateType {
|
||||
case ice.CandidateTypeHost:
|
||||
return ICECandidateTypeHost, nil
|
||||
case ice.CandidateTypeServerReflexive:
|
||||
return ICECandidateTypeSrflx, nil
|
||||
case ice.CandidateTypePeerReflexive:
|
||||
return ICECandidateTypePrflx, nil
|
||||
case ice.CandidateTypeRelay:
|
||||
return ICECandidateTypeRelay, nil
|
||||
default:
|
||||
// NOTE: this should never happen[tm]
|
||||
err := fmt.Errorf("%w: %s", errICEInvalidConvertCandidateType, candidateType.String())
|
||||
return ICECandidateType(Unknown), err
|
||||
}
|
||||
}
|
47
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecomponent.go
generated
vendored
Normal file
47
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecomponent.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package webrtc
|
||||
|
||||
// ICEComponent describes if the ice transport is used for RTP
|
||||
// (or RTCP multiplexing).
|
||||
type ICEComponent int
|
||||
|
||||
const (
|
||||
// ICEComponentRTP indicates that the ICE Transport is used for RTP (or
|
||||
// RTCP multiplexing), as defined in
|
||||
// https://tools.ietf.org/html/rfc5245#section-4.1.1.1. Protocols
|
||||
// multiplexed with RTP (e.g. data channel) share its component ID. This
|
||||
// represents the component-id value 1 when encoded in candidate-attribute.
|
||||
ICEComponentRTP ICEComponent = iota + 1
|
||||
|
||||
// ICEComponentRTCP indicates that the ICE Transport is used for RTCP as
|
||||
// defined by https://tools.ietf.org/html/rfc5245#section-4.1.1.1. This
|
||||
// represents the component-id value 2 when encoded in candidate-attribute.
|
||||
ICEComponentRTCP
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceComponentRTPStr = "rtp"
|
||||
iceComponentRTCPStr = "rtcp"
|
||||
)
|
||||
|
||||
func newICEComponent(raw string) ICEComponent {
|
||||
switch raw {
|
||||
case iceComponentRTPStr:
|
||||
return ICEComponentRTP
|
||||
case iceComponentRTCPStr:
|
||||
return ICEComponentRTCP
|
||||
default:
|
||||
return ICEComponent(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICEComponent) String() string {
|
||||
switch t {
|
||||
case ICEComponentRTP:
|
||||
return iceComponentRTPStr
|
||||
case ICEComponentRTCP:
|
||||
return iceComponentRTCPStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go
generated
vendored
Normal file
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package webrtc
|
||||
|
||||
// ICEConnectionState indicates signaling state of the ICE Connection.
|
||||
type ICEConnectionState int
|
||||
|
||||
const (
|
||||
// ICEConnectionStateNew indicates that any of the ICETransports are
|
||||
// in the "new" state and none of them are in the "checking", "disconnected"
|
||||
// or "failed" state, or all ICETransports are in the "closed" state, or
|
||||
// there are no transports.
|
||||
ICEConnectionStateNew ICEConnectionState = iota + 1
|
||||
|
||||
// ICEConnectionStateChecking indicates that any of the ICETransports
|
||||
// are in the "checking" state and none of them are in the "disconnected"
|
||||
// or "failed" state.
|
||||
ICEConnectionStateChecking
|
||||
|
||||
// ICEConnectionStateConnected indicates that all ICETransports are
|
||||
// in the "connected", "completed" or "closed" state and at least one of
|
||||
// them is in the "connected" state.
|
||||
ICEConnectionStateConnected
|
||||
|
||||
// ICEConnectionStateCompleted indicates that all ICETransports are
|
||||
// in the "completed" or "closed" state and at least one of them is in the
|
||||
// "completed" state.
|
||||
ICEConnectionStateCompleted
|
||||
|
||||
// ICEConnectionStateDisconnected indicates that any of the
|
||||
// ICETransports are in the "disconnected" state and none of them are
|
||||
// in the "failed" state.
|
||||
ICEConnectionStateDisconnected
|
||||
|
||||
// ICEConnectionStateFailed indicates that any of the ICETransports
|
||||
// are in the "failed" state.
|
||||
ICEConnectionStateFailed
|
||||
|
||||
// ICEConnectionStateClosed indicates that the PeerConnection's
|
||||
// isClosed is true.
|
||||
ICEConnectionStateClosed
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceConnectionStateNewStr = "new"
|
||||
iceConnectionStateCheckingStr = "checking"
|
||||
iceConnectionStateConnectedStr = "connected"
|
||||
iceConnectionStateCompletedStr = "completed"
|
||||
iceConnectionStateDisconnectedStr = "disconnected"
|
||||
iceConnectionStateFailedStr = "failed"
|
||||
iceConnectionStateClosedStr = "closed"
|
||||
)
|
||||
|
||||
// NewICEConnectionState takes a string and converts it to ICEConnectionState
|
||||
func NewICEConnectionState(raw string) ICEConnectionState {
|
||||
switch raw {
|
||||
case iceConnectionStateNewStr:
|
||||
return ICEConnectionStateNew
|
||||
case iceConnectionStateCheckingStr:
|
||||
return ICEConnectionStateChecking
|
||||
case iceConnectionStateConnectedStr:
|
||||
return ICEConnectionStateConnected
|
||||
case iceConnectionStateCompletedStr:
|
||||
return ICEConnectionStateCompleted
|
||||
case iceConnectionStateDisconnectedStr:
|
||||
return ICEConnectionStateDisconnected
|
||||
case iceConnectionStateFailedStr:
|
||||
return ICEConnectionStateFailed
|
||||
case iceConnectionStateClosedStr:
|
||||
return ICEConnectionStateClosed
|
||||
default:
|
||||
return ICEConnectionState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (c ICEConnectionState) String() string {
|
||||
switch c {
|
||||
case ICEConnectionStateNew:
|
||||
return iceConnectionStateNewStr
|
||||
case ICEConnectionStateChecking:
|
||||
return iceConnectionStateCheckingStr
|
||||
case ICEConnectionStateConnected:
|
||||
return iceConnectionStateConnectedStr
|
||||
case ICEConnectionStateCompleted:
|
||||
return iceConnectionStateCompletedStr
|
||||
case ICEConnectionStateDisconnected:
|
||||
return iceConnectionStateDisconnectedStr
|
||||
case ICEConnectionStateFailed:
|
||||
return iceConnectionStateFailedStr
|
||||
case ICEConnectionStateClosed:
|
||||
return iceConnectionStateClosedStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
43
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecredentialtype.go
generated
vendored
Normal file
43
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icecredentialtype.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package webrtc
|
||||
|
||||
// ICECredentialType indicates the type of credentials used to connect to
|
||||
// an ICE server.
|
||||
type ICECredentialType int
|
||||
|
||||
const (
|
||||
// ICECredentialTypePassword describes username and password based
|
||||
// credentials as described in https://tools.ietf.org/html/rfc5389.
|
||||
ICECredentialTypePassword ICECredentialType = iota
|
||||
|
||||
// ICECredentialTypeOauth describes token based credential as described
|
||||
// in https://tools.ietf.org/html/rfc7635.
|
||||
ICECredentialTypeOauth
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceCredentialTypePasswordStr = "password"
|
||||
iceCredentialTypeOauthStr = "oauth"
|
||||
)
|
||||
|
||||
func newICECredentialType(raw string) ICECredentialType {
|
||||
switch raw {
|
||||
case iceCredentialTypePasswordStr:
|
||||
return ICECredentialTypePassword
|
||||
case iceCredentialTypeOauthStr:
|
||||
return ICECredentialTypeOauth
|
||||
default:
|
||||
return ICECredentialType(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICECredentialType) String() string {
|
||||
switch t {
|
||||
case ICECredentialTypePassword:
|
||||
return iceCredentialTypePasswordStr
|
||||
case ICECredentialTypeOauth:
|
||||
return iceCredentialTypeOauthStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
367
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatherer.go
generated
vendored
Normal file
367
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatherer.go
generated
vendored
Normal file
|
@ -0,0 +1,367 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
// ICEGatherer gathers local host, server reflexive and relay
|
||||
// candidates, as well as enabling the retrieval of local Interactive
|
||||
// Connectivity Establishment (ICE) parameters which can be
|
||||
// exchanged in signaling.
|
||||
type ICEGatherer struct {
|
||||
lock sync.RWMutex
|
||||
log logging.LeveledLogger
|
||||
state ICEGathererState
|
||||
|
||||
validatedServers []*ice.URL
|
||||
gatherPolicy ICETransportPolicy
|
||||
|
||||
agent *ice.Agent
|
||||
|
||||
onLocalCandidateHandler atomic.Value // func(candidate *ICECandidate)
|
||||
onStateChangeHandler atomic.Value // func(state ICEGathererState)
|
||||
|
||||
// Used for GatheringCompletePromise
|
||||
onGatheringCompleteHandler atomic.Value // func()
|
||||
|
||||
api *API
|
||||
}
|
||||
|
||||
// NewICEGatherer creates a new NewICEGatherer.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, error) {
|
||||
var validatedServers []*ice.URL
|
||||
if len(opts.ICEServers) > 0 {
|
||||
for _, server := range opts.ICEServers {
|
||||
url, err := server.urls()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validatedServers = append(validatedServers, url...)
|
||||
}
|
||||
}
|
||||
|
||||
return &ICEGatherer{
|
||||
state: ICEGathererStateNew,
|
||||
gatherPolicy: opts.ICEGatherPolicy,
|
||||
validatedServers: validatedServers,
|
||||
api: api,
|
||||
log: api.settingEngine.LoggerFactory.NewLogger("ice"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *ICEGatherer) createAgent() error {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
if g.agent != nil || g.State() != ICEGathererStateNew {
|
||||
return nil
|
||||
}
|
||||
|
||||
candidateTypes := []ice.CandidateType{}
|
||||
if g.api.settingEngine.candidates.ICELite {
|
||||
candidateTypes = append(candidateTypes, ice.CandidateTypeHost)
|
||||
} else if g.gatherPolicy == ICETransportPolicyRelay {
|
||||
candidateTypes = append(candidateTypes, ice.CandidateTypeRelay)
|
||||
}
|
||||
|
||||
var nat1To1CandiTyp ice.CandidateType
|
||||
switch g.api.settingEngine.candidates.NAT1To1IPCandidateType {
|
||||
case ICECandidateTypeHost:
|
||||
nat1To1CandiTyp = ice.CandidateTypeHost
|
||||
case ICECandidateTypeSrflx:
|
||||
nat1To1CandiTyp = ice.CandidateTypeServerReflexive
|
||||
default:
|
||||
nat1To1CandiTyp = ice.CandidateTypeUnspecified
|
||||
}
|
||||
|
||||
mDNSMode := g.api.settingEngine.candidates.MulticastDNSMode
|
||||
if mDNSMode != ice.MulticastDNSModeDisabled && mDNSMode != ice.MulticastDNSModeQueryAndGather {
|
||||
// If enum is in state we don't recognized default to MulticastDNSModeQueryOnly
|
||||
mDNSMode = ice.MulticastDNSModeQueryOnly
|
||||
}
|
||||
|
||||
config := &ice.AgentConfig{
|
||||
Lite: g.api.settingEngine.candidates.ICELite,
|
||||
Urls: g.validatedServers,
|
||||
PortMin: g.api.settingEngine.ephemeralUDP.PortMin,
|
||||
PortMax: g.api.settingEngine.ephemeralUDP.PortMax,
|
||||
DisconnectedTimeout: g.api.settingEngine.timeout.ICEDisconnectedTimeout,
|
||||
FailedTimeout: g.api.settingEngine.timeout.ICEFailedTimeout,
|
||||
KeepaliveInterval: g.api.settingEngine.timeout.ICEKeepaliveInterval,
|
||||
LoggerFactory: g.api.settingEngine.LoggerFactory,
|
||||
CandidateTypes: candidateTypes,
|
||||
HostAcceptanceMinWait: g.api.settingEngine.timeout.ICEHostAcceptanceMinWait,
|
||||
SrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICESrflxAcceptanceMinWait,
|
||||
PrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICEPrflxAcceptanceMinWait,
|
||||
RelayAcceptanceMinWait: g.api.settingEngine.timeout.ICERelayAcceptanceMinWait,
|
||||
InterfaceFilter: g.api.settingEngine.candidates.InterfaceFilter,
|
||||
NAT1To1IPs: g.api.settingEngine.candidates.NAT1To1IPs,
|
||||
NAT1To1IPCandidateType: nat1To1CandiTyp,
|
||||
Net: g.api.settingEngine.vnet,
|
||||
MulticastDNSMode: mDNSMode,
|
||||
MulticastDNSHostName: g.api.settingEngine.candidates.MulticastDNSHostName,
|
||||
LocalUfrag: g.api.settingEngine.candidates.UsernameFragment,
|
||||
LocalPwd: g.api.settingEngine.candidates.Password,
|
||||
TCPMux: g.api.settingEngine.iceTCPMux,
|
||||
ProxyDialer: g.api.settingEngine.iceProxyDialer,
|
||||
}
|
||||
|
||||
requestedNetworkTypes := g.api.settingEngine.candidates.ICENetworkTypes
|
||||
if len(requestedNetworkTypes) == 0 {
|
||||
requestedNetworkTypes = supportedNetworkTypes()
|
||||
}
|
||||
|
||||
for _, typ := range requestedNetworkTypes {
|
||||
config.NetworkTypes = append(config.NetworkTypes, ice.NetworkType(typ))
|
||||
}
|
||||
|
||||
agent, err := ice.NewAgent(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.agent = agent
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gather ICE candidates.
|
||||
func (g *ICEGatherer) Gather() error {
|
||||
if err := g.createAgent(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.lock.Lock()
|
||||
agent := g.agent
|
||||
g.lock.Unlock()
|
||||
|
||||
g.setState(ICEGathererStateGathering)
|
||||
if err := agent.OnCandidate(func(candidate ice.Candidate) {
|
||||
onLocalCandidateHandler := func(*ICECandidate) {}
|
||||
if handler, ok := g.onLocalCandidateHandler.Load().(func(candidate *ICECandidate)); ok && handler != nil {
|
||||
onLocalCandidateHandler = handler
|
||||
}
|
||||
|
||||
onGatheringCompleteHandler := func() {}
|
||||
if handler, ok := g.onGatheringCompleteHandler.Load().(func()); ok && handler != nil {
|
||||
onGatheringCompleteHandler = handler
|
||||
}
|
||||
|
||||
if candidate != nil {
|
||||
c, err := newICECandidateFromICE(candidate)
|
||||
if err != nil {
|
||||
g.log.Warnf("Failed to convert ice.Candidate: %s", err)
|
||||
return
|
||||
}
|
||||
onLocalCandidateHandler(&c)
|
||||
} else {
|
||||
g.setState(ICEGathererStateComplete)
|
||||
|
||||
onGatheringCompleteHandler()
|
||||
onLocalCandidateHandler(nil)
|
||||
}
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return agent.GatherCandidates()
|
||||
}
|
||||
|
||||
// Close prunes all local candidates, and closes the ports.
|
||||
func (g *ICEGatherer) Close() error {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
if g.agent == nil {
|
||||
return nil
|
||||
} else if err := g.agent.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g.agent = nil
|
||||
g.setState(ICEGathererStateClosed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLocalParameters returns the ICE parameters of the ICEGatherer.
|
||||
func (g *ICEGatherer) GetLocalParameters() (ICEParameters, error) {
|
||||
if err := g.createAgent(); err != nil {
|
||||
return ICEParameters{}, err
|
||||
}
|
||||
|
||||
frag, pwd, err := g.agent.GetLocalUserCredentials()
|
||||
if err != nil {
|
||||
return ICEParameters{}, err
|
||||
}
|
||||
|
||||
return ICEParameters{
|
||||
UsernameFragment: frag,
|
||||
Password: pwd,
|
||||
ICELite: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetLocalCandidates returns the sequence of valid local candidates associated with the ICEGatherer.
|
||||
func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) {
|
||||
if err := g.createAgent(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iceCandidates, err := g.agent.GetLocalCandidates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newICECandidatesFromICE(iceCandidates)
|
||||
}
|
||||
|
||||
// OnLocalCandidate sets an event handler which fires when a new local ICE candidate is available
|
||||
// Take note that the handler is gonna be called with a nil pointer when gathering is finished.
|
||||
func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) {
|
||||
g.onLocalCandidateHandler.Store(f)
|
||||
}
|
||||
|
||||
// OnStateChange fires any time the ICEGatherer changes
|
||||
func (g *ICEGatherer) OnStateChange(f func(ICEGathererState)) {
|
||||
g.onStateChangeHandler.Store(f)
|
||||
}
|
||||
|
||||
// State indicates the current state of the ICE gatherer.
|
||||
func (g *ICEGatherer) State() ICEGathererState {
|
||||
return atomicLoadICEGathererState(&g.state)
|
||||
}
|
||||
|
||||
func (g *ICEGatherer) setState(s ICEGathererState) {
|
||||
atomicStoreICEGathererState(&g.state, s)
|
||||
|
||||
if handler, ok := g.onStateChangeHandler.Load().(func(state ICEGathererState)); ok && handler != nil {
|
||||
handler(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *ICEGatherer) getAgent() *ice.Agent {
|
||||
g.lock.RLock()
|
||||
defer g.lock.RUnlock()
|
||||
return g.agent
|
||||
}
|
||||
|
||||
func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
|
||||
agent := g.getAgent()
|
||||
if agent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
collector.Collecting()
|
||||
go func(collector *statsReportCollector, agent *ice.Agent) {
|
||||
for _, candidatePairStats := range agent.GetCandidatePairsStats() {
|
||||
collector.Collecting()
|
||||
|
||||
state, err := toStatsICECandidatePairState(candidatePairStats.State)
|
||||
if err != nil {
|
||||
g.log.Error(err.Error())
|
||||
}
|
||||
|
||||
pairID := newICECandidatePairStatsID(candidatePairStats.LocalCandidateID,
|
||||
candidatePairStats.RemoteCandidateID)
|
||||
|
||||
stats := ICECandidatePairStats{
|
||||
Timestamp: statsTimestampFrom(candidatePairStats.Timestamp),
|
||||
Type: StatsTypeCandidatePair,
|
||||
ID: pairID,
|
||||
// TransportID:
|
||||
LocalCandidateID: candidatePairStats.LocalCandidateID,
|
||||
RemoteCandidateID: candidatePairStats.RemoteCandidateID,
|
||||
State: state,
|
||||
Nominated: candidatePairStats.Nominated,
|
||||
PacketsSent: candidatePairStats.PacketsSent,
|
||||
PacketsReceived: candidatePairStats.PacketsReceived,
|
||||
BytesSent: candidatePairStats.BytesSent,
|
||||
BytesReceived: candidatePairStats.BytesReceived,
|
||||
LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp),
|
||||
LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp),
|
||||
FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp),
|
||||
LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp),
|
||||
LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp),
|
||||
TotalRoundTripTime: candidatePairStats.TotalRoundTripTime,
|
||||
CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime,
|
||||
AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate,
|
||||
AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate,
|
||||
CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount,
|
||||
RequestsReceived: candidatePairStats.RequestsReceived,
|
||||
RequestsSent: candidatePairStats.RequestsSent,
|
||||
ResponsesReceived: candidatePairStats.ResponsesReceived,
|
||||
ResponsesSent: candidatePairStats.ResponsesSent,
|
||||
RetransmissionsReceived: candidatePairStats.RetransmissionsReceived,
|
||||
RetransmissionsSent: candidatePairStats.RetransmissionsSent,
|
||||
ConsentRequestsSent: candidatePairStats.ConsentRequestsSent,
|
||||
ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp),
|
||||
}
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
|
||||
for _, candidateStats := range agent.GetLocalCandidatesStats() {
|
||||
collector.Collecting()
|
||||
|
||||
networkType, err := getNetworkType(candidateStats.NetworkType)
|
||||
if err != nil {
|
||||
g.log.Error(err.Error())
|
||||
}
|
||||
|
||||
candidateType, err := getCandidateType(candidateStats.CandidateType)
|
||||
if err != nil {
|
||||
g.log.Error(err.Error())
|
||||
}
|
||||
|
||||
stats := ICECandidateStats{
|
||||
Timestamp: statsTimestampFrom(candidateStats.Timestamp),
|
||||
ID: candidateStats.ID,
|
||||
Type: StatsTypeLocalCandidate,
|
||||
NetworkType: networkType,
|
||||
IP: candidateStats.IP,
|
||||
Port: int32(candidateStats.Port),
|
||||
Protocol: networkType.Protocol(),
|
||||
CandidateType: candidateType,
|
||||
Priority: int32(candidateStats.Priority),
|
||||
URL: candidateStats.URL,
|
||||
RelayProtocol: candidateStats.RelayProtocol,
|
||||
Deleted: candidateStats.Deleted,
|
||||
}
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
|
||||
for _, candidateStats := range agent.GetRemoteCandidatesStats() {
|
||||
collector.Collecting()
|
||||
networkType, err := getNetworkType(candidateStats.NetworkType)
|
||||
if err != nil {
|
||||
g.log.Error(err.Error())
|
||||
}
|
||||
|
||||
candidateType, err := getCandidateType(candidateStats.CandidateType)
|
||||
if err != nil {
|
||||
g.log.Error(err.Error())
|
||||
}
|
||||
|
||||
stats := ICECandidateStats{
|
||||
Timestamp: statsTimestampFrom(candidateStats.Timestamp),
|
||||
ID: candidateStats.ID,
|
||||
Type: StatsTypeRemoteCandidate,
|
||||
NetworkType: networkType,
|
||||
IP: candidateStats.IP,
|
||||
Port: int32(candidateStats.Port),
|
||||
Protocol: networkType.Protocol(),
|
||||
CandidateType: candidateType,
|
||||
Priority: int32(candidateStats.Priority),
|
||||
URL: candidateStats.URL,
|
||||
RelayProtocol: candidateStats.RelayProtocol,
|
||||
}
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
collector.Done()
|
||||
}(collector, agent)
|
||||
}
|
48
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegathererstate.go
generated
vendored
Normal file
48
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegathererstate.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ICEGathererState represents the current state of the ICE gatherer.
|
||||
type ICEGathererState uint32
|
||||
|
||||
const (
|
||||
// ICEGathererStateNew indicates object has been created but
|
||||
// gather() has not been called.
|
||||
ICEGathererStateNew ICEGathererState = iota + 1
|
||||
|
||||
// ICEGathererStateGathering indicates gather() has been called,
|
||||
// and the ICEGatherer is in the process of gathering candidates.
|
||||
ICEGathererStateGathering
|
||||
|
||||
// ICEGathererStateComplete indicates the ICEGatherer has completed gathering.
|
||||
ICEGathererStateComplete
|
||||
|
||||
// ICEGathererStateClosed indicates the closed state can only be entered
|
||||
// when the ICEGatherer has been closed intentionally by calling close().
|
||||
ICEGathererStateClosed
|
||||
)
|
||||
|
||||
func (s ICEGathererState) String() string {
|
||||
switch s {
|
||||
case ICEGathererStateNew:
|
||||
return "new"
|
||||
case ICEGathererStateGathering:
|
||||
return "gathering"
|
||||
case ICEGathererStateComplete:
|
||||
return "complete"
|
||||
case ICEGathererStateClosed:
|
||||
return "closed"
|
||||
default:
|
||||
return unknownStr
|
||||
}
|
||||
}
|
||||
|
||||
func atomicStoreICEGathererState(state *ICEGathererState, newState ICEGathererState) {
|
||||
atomic.StoreUint32((*uint32)(state), uint32(newState))
|
||||
}
|
||||
|
||||
func atomicLoadICEGathererState(state *ICEGathererState) ICEGathererState {
|
||||
return ICEGathererState(atomic.LoadUint32((*uint32)(state)))
|
||||
}
|
53
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatheringstate.go
generated
vendored
Normal file
53
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatheringstate.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
package webrtc
|
||||
|
||||
// ICEGatheringState describes the state of the candidate gathering process.
|
||||
type ICEGatheringState int
|
||||
|
||||
const (
|
||||
// ICEGatheringStateNew indicates that any of the ICETransports are
|
||||
// in the "new" gathering state and none of the transports are in the
|
||||
// "gathering" state, or there are no transports.
|
||||
ICEGatheringStateNew ICEGatheringState = iota + 1
|
||||
|
||||
// ICEGatheringStateGathering indicates that any of the ICETransports
|
||||
// are in the "gathering" state.
|
||||
ICEGatheringStateGathering
|
||||
|
||||
// ICEGatheringStateComplete indicates that at least one ICETransport
|
||||
// exists, and all ICETransports are in the "completed" gathering state.
|
||||
ICEGatheringStateComplete
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceGatheringStateNewStr = "new"
|
||||
iceGatheringStateGatheringStr = "gathering"
|
||||
iceGatheringStateCompleteStr = "complete"
|
||||
)
|
||||
|
||||
// NewICEGatheringState takes a string and converts it to ICEGatheringState
|
||||
func NewICEGatheringState(raw string) ICEGatheringState {
|
||||
switch raw {
|
||||
case iceGatheringStateNewStr:
|
||||
return ICEGatheringStateNew
|
||||
case iceGatheringStateGatheringStr:
|
||||
return ICEGatheringStateGathering
|
||||
case iceGatheringStateCompleteStr:
|
||||
return ICEGatheringStateComplete
|
||||
default:
|
||||
return ICEGatheringState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICEGatheringState) String() string {
|
||||
switch t {
|
||||
case ICEGatheringStateNew:
|
||||
return iceGatheringStateNewStr
|
||||
case ICEGatheringStateGathering:
|
||||
return iceGatheringStateGatheringStr
|
||||
case ICEGatheringStateComplete:
|
||||
return iceGatheringStateCompleteStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatheroptions.go
generated
vendored
Normal file
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icegatheroptions.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package webrtc
|
||||
|
||||
// ICEGatherOptions provides options relating to the gathering of ICE candidates.
|
||||
type ICEGatherOptions struct {
|
||||
ICEServers []ICEServer
|
||||
ICEGatherPolicy ICETransportPolicy
|
||||
}
|
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceparameters.go
generated
vendored
Normal file
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package webrtc
|
||||
|
||||
// ICEParameters includes the ICE username fragment
|
||||
// and password and other ICE-related parameters.
|
||||
type ICEParameters struct {
|
||||
UsernameFragment string `json:"usernameFragment"`
|
||||
Password string `json:"password"`
|
||||
ICELite bool `json:"iceLite"`
|
||||
}
|
47
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceprotocol.go
generated
vendored
Normal file
47
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceprotocol.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ICEProtocol indicates the transport protocol type that is used in the
|
||||
// ice.URL structure.
|
||||
type ICEProtocol int
|
||||
|
||||
const (
|
||||
// ICEProtocolUDP indicates the URL uses a UDP transport.
|
||||
ICEProtocolUDP ICEProtocol = iota + 1
|
||||
|
||||
// ICEProtocolTCP indicates the URL uses a TCP transport.
|
||||
ICEProtocolTCP
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceProtocolUDPStr = "udp"
|
||||
iceProtocolTCPStr = "tcp"
|
||||
)
|
||||
|
||||
// NewICEProtocol takes a string and converts it to ICEProtocol
|
||||
func NewICEProtocol(raw string) (ICEProtocol, error) {
|
||||
switch {
|
||||
case strings.EqualFold(iceProtocolUDPStr, raw):
|
||||
return ICEProtocolUDP, nil
|
||||
case strings.EqualFold(iceProtocolTCPStr, raw):
|
||||
return ICEProtocolTCP, nil
|
||||
default:
|
||||
return ICEProtocol(Unknown), fmt.Errorf("%w: %s", errICEProtocolUnknown, raw)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICEProtocol) String() string {
|
||||
switch t {
|
||||
case ICEProtocolUDP:
|
||||
return iceProtocolUDPStr
|
||||
case ICEProtocolTCP:
|
||||
return iceProtocolTCPStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
45
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icerole.go
generated
vendored
Normal file
45
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icerole.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package webrtc
|
||||
|
||||
// ICERole describes the role ice.Agent is playing in selecting the
|
||||
// preferred the candidate pair.
|
||||
type ICERole int
|
||||
|
||||
const (
|
||||
// ICERoleControlling indicates that the ICE agent that is responsible
|
||||
// for selecting the final choice of candidate pairs and signaling them
|
||||
// through STUN and an updated offer, if needed. In any session, one agent
|
||||
// is always controlling. The other is the controlled agent.
|
||||
ICERoleControlling ICERole = iota + 1
|
||||
|
||||
// ICERoleControlled indicates that an ICE agent that waits for the
|
||||
// controlling agent to select the final choice of candidate pairs.
|
||||
ICERoleControlled
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceRoleControllingStr = "controlling"
|
||||
iceRoleControlledStr = "controlled"
|
||||
)
|
||||
|
||||
func newICERole(raw string) ICERole {
|
||||
switch raw {
|
||||
case iceRoleControllingStr:
|
||||
return ICERoleControlling
|
||||
case iceRoleControlledStr:
|
||||
return ICERoleControlled
|
||||
default:
|
||||
return ICERole(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICERole) String() string {
|
||||
switch t {
|
||||
case ICERoleControlling:
|
||||
return iceRoleControllingStr
|
||||
case ICERoleControlled:
|
||||
return iceRoleControlledStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
68
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceserver.go
generated
vendored
Normal file
68
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceserver.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// ICEServer describes a single STUN and TURN server that can be used by
|
||||
// the ICEAgent to establish a connection with a peer.
|
||||
type ICEServer struct {
|
||||
URLs []string `json:"urls"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Credential interface{} `json:"credential,omitempty"`
|
||||
CredentialType ICECredentialType `json:"credentialType,omitempty"`
|
||||
}
|
||||
|
||||
func (s ICEServer) parseURL(i int) (*ice.URL, error) {
|
||||
return ice.ParseURL(s.URLs[i])
|
||||
}
|
||||
|
||||
func (s ICEServer) validate() error {
|
||||
_, err := s.urls()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s ICEServer) urls() ([]*ice.URL, error) {
|
||||
urls := []*ice.URL{}
|
||||
|
||||
for i := range s.URLs {
|
||||
url, err := s.parseURL(i)
|
||||
if err != nil {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: err}
|
||||
}
|
||||
|
||||
if url.Scheme == ice.SchemeTypeTURN || url.Scheme == ice.SchemeTypeTURNS {
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2)
|
||||
if s.Username == "" || s.Credential == nil {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}
|
||||
}
|
||||
url.Username = s.Username
|
||||
|
||||
switch s.CredentialType {
|
||||
case ICECredentialTypePassword:
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.3)
|
||||
password, ok := s.Credential.(string)
|
||||
if !ok {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
|
||||
}
|
||||
url.Password = password
|
||||
|
||||
case ICECredentialTypeOauth:
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.4)
|
||||
if _, ok := s.Credential.(OAuthCredential); !ok {
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
|
||||
}
|
||||
}
|
||||
|
||||
urls = append(urls, url)
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
42
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceserver_js.go
generated
vendored
Normal file
42
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/iceserver_js.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
)
|
||||
|
||||
// ICEServer describes a single STUN and TURN server that can be used by
|
||||
// the ICEAgent to establish a connection with a peer.
|
||||
type ICEServer struct {
|
||||
URLs []string
|
||||
Username string
|
||||
// Note: TURN is not supported in the WASM bindings yet
|
||||
Credential interface{}
|
||||
CredentialType ICECredentialType
|
||||
}
|
||||
|
||||
func (s ICEServer) parseURL(i int) (*ice.URL, error) {
|
||||
return ice.ParseURL(s.URLs[i])
|
||||
}
|
||||
|
||||
func (s ICEServer) validate() ([]*ice.URL, error) {
|
||||
urls := []*ice.URL{}
|
||||
|
||||
for i := range s.URLs {
|
||||
url, err := s.parseURL(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if url.Scheme == ice.SchemeTypeTURN || url.Scheme == ice.SchemeTypeTURNS {
|
||||
return nil, errors.New("TURN is not currently supported in the JavaScript/Wasm bindings")
|
||||
}
|
||||
|
||||
urls = append(urls, url)
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
18
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetcp.go
generated
vendored
Normal file
18
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetcp.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/logging"
|
||||
)
|
||||
|
||||
// NewICETCPMux creates a new instance of ice.TCPMuxDefault. It enables use of
|
||||
// passive ICE TCP candidates.
|
||||
func NewICETCPMux(logger logging.LeveledLogger, listener net.Listener, readBufferSize int) ice.TCPMux {
|
||||
return ice.NewTCPMuxDefault(ice.TCPMuxParams{
|
||||
Listener: listener,
|
||||
Logger: logger,
|
||||
ReadBufferSize: readBufferSize,
|
||||
})
|
||||
}
|
368
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransport.go
generated
vendored
Normal file
368
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransport.go
generated
vendored
Normal file
|
@ -0,0 +1,368 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/webrtc/v3/internal/mux"
|
||||
)
|
||||
|
||||
// ICETransport allows an application access to information about the ICE
|
||||
// transport over which packets are sent and received.
|
||||
type ICETransport struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
role ICERole
|
||||
// Component ICEComponent
|
||||
// State ICETransportState
|
||||
// gatheringState ICEGathererState
|
||||
|
||||
onConnectionStateChangeHandler atomic.Value // func(ICETransportState)
|
||||
onSelectedCandidatePairChangeHandler atomic.Value // func(*ICECandidatePair)
|
||||
|
||||
state atomic.Value // ICETransportState
|
||||
|
||||
gatherer *ICEGatherer
|
||||
conn *ice.Conn
|
||||
mux *mux.Mux
|
||||
|
||||
ctx context.Context
|
||||
ctxCancel func()
|
||||
|
||||
loggerFactory logging.LoggerFactory
|
||||
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// func (t *ICETransport) GetLocalCandidates() []ICECandidate {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func (t *ICETransport) GetRemoteCandidates() []ICECandidate {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func (t *ICETransport) GetSelectedCandidatePair() ICECandidatePair {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func (t *ICETransport) GetLocalParameters() ICEParameters {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func (t *ICETransport) GetRemoteParameters() ICEParameters {
|
||||
//
|
||||
// }
|
||||
|
||||
// NewICETransport creates a new NewICETransport.
|
||||
func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) *ICETransport {
|
||||
iceTransport := &ICETransport{
|
||||
gatherer: gatherer,
|
||||
loggerFactory: loggerFactory,
|
||||
log: loggerFactory.NewLogger("ortc"),
|
||||
}
|
||||
iceTransport.setState(ICETransportStateNew)
|
||||
return iceTransport
|
||||
}
|
||||
|
||||
// Start incoming connectivity checks based on its configured role.
|
||||
func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *ICERole) error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
if t.State() != ICETransportStateNew {
|
||||
return errICETransportNotInNew
|
||||
}
|
||||
|
||||
if gatherer != nil {
|
||||
t.gatherer = gatherer
|
||||
}
|
||||
|
||||
if err := t.ensureGatherer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return fmt.Errorf("%w: unable to start ICETransport", errICEAgentNotExist)
|
||||
}
|
||||
|
||||
if err := agent.OnConnectionStateChange(func(iceState ice.ConnectionState) {
|
||||
state := newICETransportStateFromICE(iceState)
|
||||
|
||||
t.setState(state)
|
||||
t.onConnectionStateChange(state)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := agent.OnSelectedCandidatePairChange(func(local, remote ice.Candidate) {
|
||||
candidates, err := newICECandidatesFromICE([]ice.Candidate{local, remote})
|
||||
if err != nil {
|
||||
t.log.Warnf("%w: %s", errICECandiatesCoversionFailed, err)
|
||||
return
|
||||
}
|
||||
t.onSelectedCandidatePairChange(NewICECandidatePair(&candidates[0], &candidates[1]))
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if role == nil {
|
||||
controlled := ICERoleControlled
|
||||
role = &controlled
|
||||
}
|
||||
t.role = *role
|
||||
|
||||
t.ctx, t.ctxCancel = context.WithCancel(context.Background())
|
||||
|
||||
// Drop the lock here to allow ICE candidates to be
|
||||
// added so that the agent can complete a connection
|
||||
t.lock.Unlock()
|
||||
|
||||
var iceConn *ice.Conn
|
||||
var err error
|
||||
switch *role {
|
||||
case ICERoleControlling:
|
||||
iceConn, err = agent.Dial(t.ctx,
|
||||
params.UsernameFragment,
|
||||
params.Password)
|
||||
|
||||
case ICERoleControlled:
|
||||
iceConn, err = agent.Accept(t.ctx,
|
||||
params.UsernameFragment,
|
||||
params.Password)
|
||||
|
||||
default:
|
||||
err = errICERoleUnknown
|
||||
}
|
||||
|
||||
// Reacquire the lock to set the connection/mux
|
||||
t.lock.Lock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.conn = iceConn
|
||||
|
||||
config := mux.Config{
|
||||
Conn: t.conn,
|
||||
BufferSize: receiveMTU,
|
||||
LoggerFactory: t.loggerFactory,
|
||||
}
|
||||
t.mux = mux.NewMux(config)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// restart is not exposed currently because ORTC has users create a whole new ICETransport
|
||||
// so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs
|
||||
func (t *ICETransport) restart() error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return fmt.Errorf("%w: unable to restart ICETransport", errICEAgentNotExist)
|
||||
}
|
||||
|
||||
if err := agent.Restart(t.gatherer.api.settingEngine.candidates.UsernameFragment, t.gatherer.api.settingEngine.candidates.Password); err != nil {
|
||||
return err
|
||||
}
|
||||
return t.gatherer.Gather()
|
||||
}
|
||||
|
||||
// Stop irreversibly stops the ICETransport.
|
||||
func (t *ICETransport) Stop() error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
t.setState(ICETransportStateClosed)
|
||||
|
||||
if t.ctxCancel != nil {
|
||||
t.ctxCancel()
|
||||
}
|
||||
|
||||
if t.mux != nil {
|
||||
return t.mux.Close()
|
||||
} else if t.gatherer != nil {
|
||||
return t.gatherer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnSelectedCandidatePairChange sets a handler that is invoked when a new
|
||||
// ICE candidate pair is selected
|
||||
func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandidatePair)) {
|
||||
t.onSelectedCandidatePairChangeHandler.Store(f)
|
||||
}
|
||||
|
||||
func (t *ICETransport) onSelectedCandidatePairChange(pair *ICECandidatePair) {
|
||||
handler := t.onSelectedCandidatePairChangeHandler.Load()
|
||||
if handler != nil {
|
||||
handler.(func(*ICECandidatePair))(pair)
|
||||
}
|
||||
}
|
||||
|
||||
// OnConnectionStateChange sets a handler that is fired when the ICE
|
||||
// connection state changes.
|
||||
func (t *ICETransport) OnConnectionStateChange(f func(ICETransportState)) {
|
||||
t.onConnectionStateChangeHandler.Store(f)
|
||||
}
|
||||
|
||||
func (t *ICETransport) onConnectionStateChange(state ICETransportState) {
|
||||
handler := t.onConnectionStateChangeHandler.Load()
|
||||
if handler != nil {
|
||||
handler.(func(ICETransportState))(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Role indicates the current role of the ICE transport.
|
||||
func (t *ICETransport) Role() ICERole {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
return t.role
|
||||
}
|
||||
|
||||
// SetRemoteCandidates sets the sequence of candidates associated with the remote ICETransport.
|
||||
func (t *ICETransport) SetRemoteCandidates(remoteCandidates []ICECandidate) error {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
if err := t.ensureGatherer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return fmt.Errorf("%w: unable to set remote candidates", errICEAgentNotExist)
|
||||
}
|
||||
|
||||
for _, c := range remoteCandidates {
|
||||
i, err := c.toICE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = agent.AddRemoteCandidate(i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRemoteCandidate adds a candidate associated with the remote ICETransport.
|
||||
func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
var (
|
||||
c ice.Candidate
|
||||
err error
|
||||
)
|
||||
|
||||
if err = t.ensureGatherer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if remoteCandidate != nil {
|
||||
if c, err = remoteCandidate.toICE(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return fmt.Errorf("%w: unable to add remote candidates", errICEAgentNotExist)
|
||||
}
|
||||
|
||||
return agent.AddRemoteCandidate(c)
|
||||
}
|
||||
|
||||
// State returns the current ice transport state.
|
||||
func (t *ICETransport) State() ICETransportState {
|
||||
if v := t.state.Load(); v != nil {
|
||||
return v.(ICETransportState)
|
||||
}
|
||||
return ICETransportState(0)
|
||||
}
|
||||
|
||||
func (t *ICETransport) setState(i ICETransportState) {
|
||||
t.state.Store(i)
|
||||
}
|
||||
|
||||
// NewEndpoint registers a new endpoint on the underlying mux.
|
||||
func (t *ICETransport) NewEndpoint(f mux.MatchFunc) *mux.Endpoint {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
return t.mux.NewEndpoint(f)
|
||||
}
|
||||
|
||||
func (t *ICETransport) ensureGatherer() error {
|
||||
if t.gatherer == nil {
|
||||
return errICEGathererNotStarted
|
||||
} else if t.gatherer.getAgent() == nil {
|
||||
if err := t.gatherer.createAgent(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ICETransport) collectStats(collector *statsReportCollector) {
|
||||
t.lock.Lock()
|
||||
conn := t.conn
|
||||
t.lock.Unlock()
|
||||
|
||||
collector.Collecting()
|
||||
|
||||
stats := TransportStats{
|
||||
Timestamp: statsTimestampFrom(time.Now()),
|
||||
Type: StatsTypeTransport,
|
||||
ID: "iceTransport",
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
stats.BytesSent = conn.BytesSent()
|
||||
stats.BytesReceived = conn.BytesReceived()
|
||||
}
|
||||
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
|
||||
func (t *ICETransport) haveRemoteCredentialsChange(newUfrag, newPwd string) bool {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
uFrag, uPwd, err := agent.GetRemoteUserCredentials()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return uFrag != newUfrag || uPwd != newPwd
|
||||
}
|
||||
|
||||
func (t *ICETransport) setRemoteCredentials(newUfrag, newPwd string) error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
agent := t.gatherer.getAgent()
|
||||
if agent == nil {
|
||||
return fmt.Errorf("%w: unable to SetRemoteCredentials", errICEAgentNotExist)
|
||||
}
|
||||
|
||||
return agent.SetRemoteCredentials(newUfrag, newPwd)
|
||||
}
|
65
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go
generated
vendored
Normal file
65
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// ICETransportPolicy defines the ICE candidate policy surface the
|
||||
// permitted candidates. Only these candidates are used for connectivity checks.
|
||||
type ICETransportPolicy int
|
||||
|
||||
// ICEGatherPolicy is the ORTC equivalent of ICETransportPolicy
|
||||
type ICEGatherPolicy = ICETransportPolicy
|
||||
|
||||
const (
|
||||
// ICETransportPolicyAll indicates any type of candidate is used.
|
||||
ICETransportPolicyAll ICETransportPolicy = iota
|
||||
|
||||
// ICETransportPolicyRelay indicates only media relay candidates such
|
||||
// as candidates passing through a TURN server are used.
|
||||
ICETransportPolicyRelay
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
iceTransportPolicyRelayStr = "relay"
|
||||
iceTransportPolicyAllStr = "all"
|
||||
)
|
||||
|
||||
// NewICETransportPolicy takes a string and converts it to ICETransportPolicy
|
||||
func NewICETransportPolicy(raw string) ICETransportPolicy {
|
||||
switch raw {
|
||||
case iceTransportPolicyRelayStr:
|
||||
return ICETransportPolicyRelay
|
||||
case iceTransportPolicyAllStr:
|
||||
return ICETransportPolicyAll
|
||||
default:
|
||||
return ICETransportPolicy(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t ICETransportPolicy) String() string {
|
||||
switch t {
|
||||
case ICETransportPolicyRelay:
|
||||
return iceTransportPolicyRelayStr
|
||||
case ICETransportPolicyAll:
|
||||
return iceTransportPolicyAllStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (t *ICETransportPolicy) UnmarshalJSON(b []byte) error {
|
||||
var val string
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
*t = NewICETransportPolicy(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (t ICETransportPolicy) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
107
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransportstate.go
generated
vendored
Normal file
107
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/icetransportstate.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
package webrtc
|
||||
|
||||
import "github.com/pion/ice/v2"
|
||||
|
||||
// ICETransportState represents the current state of the ICE transport.
|
||||
type ICETransportState int
|
||||
|
||||
const (
|
||||
// ICETransportStateNew indicates the ICETransport is waiting
|
||||
// for remote candidates to be supplied.
|
||||
ICETransportStateNew = iota + 1
|
||||
|
||||
// ICETransportStateChecking indicates the ICETransport has
|
||||
// received at least one remote candidate, and a local and remote
|
||||
// ICECandidateComplete dictionary was not added as the last candidate.
|
||||
ICETransportStateChecking
|
||||
|
||||
// ICETransportStateConnected indicates the ICETransport has
|
||||
// received a response to an outgoing connectivity check, or has
|
||||
// received incoming DTLS/media after a successful response to an
|
||||
// incoming connectivity check, but is still checking other candidate
|
||||
// pairs to see if there is a better connection.
|
||||
ICETransportStateConnected
|
||||
|
||||
// ICETransportStateCompleted indicates the ICETransport tested
|
||||
// all appropriate candidate pairs and at least one functioning
|
||||
// candidate pair has been found.
|
||||
ICETransportStateCompleted
|
||||
|
||||
// ICETransportStateFailed indicates the ICETransport the last
|
||||
// candidate was added and all appropriate candidate pairs have either
|
||||
// failed connectivity checks or have lost consent.
|
||||
ICETransportStateFailed
|
||||
|
||||
// ICETransportStateDisconnected indicates the ICETransport has received
|
||||
// at least one local and remote candidate, but the final candidate was
|
||||
// received yet and all appropriate candidate pairs thus far have been
|
||||
// tested and failed.
|
||||
ICETransportStateDisconnected
|
||||
|
||||
// ICETransportStateClosed indicates the ICETransport has shut down
|
||||
// and is no longer responding to STUN requests.
|
||||
ICETransportStateClosed
|
||||
)
|
||||
|
||||
func (c ICETransportState) String() string {
|
||||
switch c {
|
||||
case ICETransportStateNew:
|
||||
return "new"
|
||||
case ICETransportStateChecking:
|
||||
return "checking"
|
||||
case ICETransportStateConnected:
|
||||
return "connected"
|
||||
case ICETransportStateCompleted:
|
||||
return "completed"
|
||||
case ICETransportStateFailed:
|
||||
return "failed"
|
||||
case ICETransportStateDisconnected:
|
||||
return "disconnected"
|
||||
case ICETransportStateClosed:
|
||||
return "closed"
|
||||
default:
|
||||
return unknownStr
|
||||
}
|
||||
}
|
||||
|
||||
func newICETransportStateFromICE(i ice.ConnectionState) ICETransportState {
|
||||
switch i {
|
||||
case ice.ConnectionStateNew:
|
||||
return ICETransportStateNew
|
||||
case ice.ConnectionStateChecking:
|
||||
return ICETransportStateChecking
|
||||
case ice.ConnectionStateConnected:
|
||||
return ICETransportStateConnected
|
||||
case ice.ConnectionStateCompleted:
|
||||
return ICETransportStateCompleted
|
||||
case ice.ConnectionStateFailed:
|
||||
return ICETransportStateFailed
|
||||
case ice.ConnectionStateDisconnected:
|
||||
return ICETransportStateDisconnected
|
||||
case ice.ConnectionStateClosed:
|
||||
return ICETransportStateClosed
|
||||
default:
|
||||
return ICETransportState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (c ICETransportState) toICE() ice.ConnectionState {
|
||||
switch c {
|
||||
case ICETransportStateNew:
|
||||
return ice.ConnectionStateNew
|
||||
case ICETransportStateChecking:
|
||||
return ice.ConnectionStateChecking
|
||||
case ICETransportStateConnected:
|
||||
return ice.ConnectionStateConnected
|
||||
case ICETransportStateCompleted:
|
||||
return ice.ConnectionStateCompleted
|
||||
case ICETransportStateFailed:
|
||||
return ice.ConnectionStateFailed
|
||||
case ICETransportStateDisconnected:
|
||||
return ice.ConnectionStateDisconnected
|
||||
case ICETransportStateClosed:
|
||||
return ice.ConnectionStateClosed
|
||||
default:
|
||||
return ice.ConnectionState(Unknown)
|
||||
}
|
||||
}
|
85
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/interceptor.go
generated
vendored
Normal file
85
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/interceptor.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/interceptor/pkg/nack"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// RegisterDefaultInterceptors will register some useful interceptors.
|
||||
// If you want to customize which interceptors are loaded, you should copy the
|
||||
// code from this method and remove unwanted interceptors.
|
||||
func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error {
|
||||
if err := ConfigureNack(mediaEngine, interceptorRegistry); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureNack will setup everything necessary for handling generating/responding to nack messages.
|
||||
func ConfigureNack(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error {
|
||||
generator, err := nack.NewGeneratorInterceptor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responder, err := nack.NewResponderInterceptor()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack"}, RTPCodecTypeVideo)
|
||||
mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack", Parameter: "pli"}, RTPCodecTypeVideo)
|
||||
interceptorRegistry.Add(responder)
|
||||
interceptorRegistry.Add(generator)
|
||||
return nil
|
||||
}
|
||||
|
||||
type interceptorToTrackLocalWriter struct{ interceptor atomic.Value } // interceptor.RTPWriter }
|
||||
|
||||
func (i *interceptorToTrackLocalWriter) WriteRTP(header *rtp.Header, payload []byte) (int, error) {
|
||||
if writer, ok := i.interceptor.Load().(interceptor.RTPWriter); ok && writer != nil {
|
||||
return writer.Write(header, payload, interceptor.Attributes{})
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) {
|
||||
packet := &rtp.Packet{}
|
||||
if err := packet.Unmarshal(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return i.WriteRTP(&packet.Header, packet.Payload)
|
||||
}
|
||||
|
||||
func createStreamInfo(id string, ssrc SSRC, payloadType PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) interceptor.StreamInfo {
|
||||
headerExtensions := make([]interceptor.RTPHeaderExtension, 0, len(webrtcHeaderExtensions))
|
||||
for _, h := range webrtcHeaderExtensions {
|
||||
headerExtensions = append(headerExtensions, interceptor.RTPHeaderExtension{ID: h.ID, URI: h.URI})
|
||||
}
|
||||
|
||||
feedbacks := make([]interceptor.RTCPFeedback, 0, len(codec.RTCPFeedback))
|
||||
for _, f := range codec.RTCPFeedback {
|
||||
feedbacks = append(feedbacks, interceptor.RTCPFeedback{Type: f.Type, Parameter: f.Parameter})
|
||||
}
|
||||
|
||||
return interceptor.StreamInfo{
|
||||
ID: id,
|
||||
Attributes: interceptor.Attributes{},
|
||||
SSRC: uint32(ssrc),
|
||||
PayloadType: uint8(payloadType),
|
||||
RTPHeaderExtensions: headerExtensions,
|
||||
MimeType: codec.MimeType,
|
||||
ClockRate: codec.ClockRate,
|
||||
Channels: codec.Channels,
|
||||
SDPFmtpLine: codec.SDPFmtpLine,
|
||||
RTCPFeedback: feedbacks,
|
||||
}
|
||||
}
|
75
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go
generated
vendored
Normal file
75
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
package mux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/transport/packetio"
|
||||
)
|
||||
|
||||
// Endpoint implements net.Conn. It is used to read muxed packets.
|
||||
type Endpoint struct {
|
||||
mux *Mux
|
||||
buffer *packetio.Buffer
|
||||
}
|
||||
|
||||
// Close unregisters the endpoint from the Mux
|
||||
func (e *Endpoint) Close() (err error) {
|
||||
err = e.close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.mux.RemoveEndpoint(e)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Endpoint) close() error {
|
||||
return e.buffer.Close()
|
||||
}
|
||||
|
||||
// Read reads a packet of len(p) bytes from the underlying conn
|
||||
// that are matched by the associated MuxFunc
|
||||
func (e *Endpoint) Read(p []byte) (int, error) {
|
||||
return e.buffer.Read(p)
|
||||
}
|
||||
|
||||
// Write writes len(p) bytes to the underlying conn
|
||||
func (e *Endpoint) Write(p []byte) (int, error) {
|
||||
n, err := e.mux.nextConn.Write(p)
|
||||
if errors.Is(err, ice.ErrNoCandidatePairs) {
|
||||
return 0, nil
|
||||
} else if errors.Is(err, ice.ErrClosed) {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// LocalAddr is a stub
|
||||
func (e *Endpoint) LocalAddr() net.Addr {
|
||||
return e.mux.nextConn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr is a stub
|
||||
func (e *Endpoint) RemoteAddr() net.Addr {
|
||||
return e.mux.nextConn.RemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline is a stub
|
||||
func (e *Endpoint) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline is a stub
|
||||
func (e *Endpoint) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline is a stub
|
||||
func (e *Endpoint) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
145
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go
generated
vendored
Normal file
145
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Package mux multiplexes packets on a single socket (RFC7983)
|
||||
package mux
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/transport/packetio"
|
||||
)
|
||||
|
||||
// The maximum amount of data that can be buffered before returning errors.
|
||||
const maxBufferSize = 1000 * 1000 // 1MB
|
||||
|
||||
// Config collects the arguments to mux.Mux construction into
|
||||
// a single structure
|
||||
type Config struct {
|
||||
Conn net.Conn
|
||||
BufferSize int
|
||||
LoggerFactory logging.LoggerFactory
|
||||
}
|
||||
|
||||
// Mux allows multiplexing
|
||||
type Mux struct {
|
||||
lock sync.RWMutex
|
||||
nextConn net.Conn
|
||||
endpoints map[*Endpoint]MatchFunc
|
||||
bufferSize int
|
||||
closedCh chan struct{}
|
||||
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// NewMux creates a new Mux
|
||||
func NewMux(config Config) *Mux {
|
||||
m := &Mux{
|
||||
nextConn: config.Conn,
|
||||
endpoints: make(map[*Endpoint]MatchFunc),
|
||||
bufferSize: config.BufferSize,
|
||||
closedCh: make(chan struct{}),
|
||||
log: config.LoggerFactory.NewLogger("mux"),
|
||||
}
|
||||
|
||||
go m.readLoop()
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new Endpoint
|
||||
func (m *Mux) NewEndpoint(f MatchFunc) *Endpoint {
|
||||
e := &Endpoint{
|
||||
mux: m,
|
||||
buffer: packetio.NewBuffer(),
|
||||
}
|
||||
|
||||
// Set a maximum size of the buffer in bytes.
|
||||
// NOTE: We actually won't get anywhere close to this limit.
|
||||
// SRTP will constantly read from the endpoint and drop packets if it's full.
|
||||
e.buffer.SetLimitSize(maxBufferSize)
|
||||
|
||||
m.lock.Lock()
|
||||
m.endpoints[e] = f
|
||||
m.lock.Unlock()
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// RemoveEndpoint removes an endpoint from the Mux
|
||||
func (m *Mux) RemoveEndpoint(e *Endpoint) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
delete(m.endpoints, e)
|
||||
}
|
||||
|
||||
// Close closes the Mux and all associated Endpoints.
|
||||
func (m *Mux) Close() error {
|
||||
m.lock.Lock()
|
||||
for e := range m.endpoints {
|
||||
err := e.close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(m.endpoints, e)
|
||||
}
|
||||
m.lock.Unlock()
|
||||
|
||||
err := m.nextConn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for readLoop to end
|
||||
<-m.closedCh
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mux) readLoop() {
|
||||
defer func() {
|
||||
close(m.closedCh)
|
||||
}()
|
||||
|
||||
buf := make([]byte, m.bufferSize)
|
||||
for {
|
||||
n, err := m.nextConn.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = m.dispatch(buf[:n])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mux) dispatch(buf []byte) error {
|
||||
var endpoint *Endpoint
|
||||
|
||||
m.lock.Lock()
|
||||
for e, f := range m.endpoints {
|
||||
if f(buf) {
|
||||
endpoint = e
|
||||
break
|
||||
}
|
||||
}
|
||||
m.lock.Unlock()
|
||||
|
||||
if endpoint == nil {
|
||||
if len(buf) > 0 {
|
||||
m.log.Warnf("Warning: mux: no endpoint for packet starting with %d\n", buf[0])
|
||||
} else {
|
||||
m.log.Warnf("Warning: mux: no endpoint for zero length packet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := endpoint.buffer.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
101
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go
generated
vendored
Normal file
101
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
package mux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// MatchFunc allows custom logic for mapping packets to an Endpoint
|
||||
type MatchFunc func([]byte) bool
|
||||
|
||||
// MatchAll always returns true
|
||||
func MatchAll(b []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// MatchNone always returns false
|
||||
func MatchNone(b []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MatchRange is a MatchFunc that accepts packets with the first byte in [lower..upper]
|
||||
func MatchRange(lower, upper byte) MatchFunc {
|
||||
return func(buf []byte) bool {
|
||||
if len(buf) < 1 {
|
||||
return false
|
||||
}
|
||||
b := buf[0]
|
||||
return b >= lower && b <= upper
|
||||
}
|
||||
}
|
||||
|
||||
// MatchFuncs as described in RFC7983
|
||||
// https://tools.ietf.org/html/rfc7983
|
||||
// +----------------+
|
||||
// | [0..3] -+--> forward to STUN
|
||||
// | |
|
||||
// | [16..19] -+--> forward to ZRTP
|
||||
// | |
|
||||
// packet --> | [20..63] -+--> forward to DTLS
|
||||
// | |
|
||||
// | [64..79] -+--> forward to TURN Channel
|
||||
// | |
|
||||
// | [128..191] -+--> forward to RTP/RTCP
|
||||
// +----------------+
|
||||
|
||||
// MatchSTUN is a MatchFunc that accepts packets with the first byte in [0..3]
|
||||
// as defied in RFC7983
|
||||
func MatchSTUN(b []byte) bool {
|
||||
return MatchRange(0, 3)(b)
|
||||
}
|
||||
|
||||
// MatchZRTP is a MatchFunc that accepts packets with the first byte in [16..19]
|
||||
// as defied in RFC7983
|
||||
func MatchZRTP(b []byte) bool {
|
||||
return MatchRange(16, 19)(b)
|
||||
}
|
||||
|
||||
// MatchDTLS is a MatchFunc that accepts packets with the first byte in [20..63]
|
||||
// as defied in RFC7983
|
||||
func MatchDTLS(b []byte) bool {
|
||||
return MatchRange(20, 63)(b)
|
||||
}
|
||||
|
||||
// MatchTURN is a MatchFunc that accepts packets with the first byte in [64..79]
|
||||
// as defied in RFC7983
|
||||
func MatchTURN(b []byte) bool {
|
||||
return MatchRange(64, 79)(b)
|
||||
}
|
||||
|
||||
// MatchSRTPOrSRTCP is a MatchFunc that accepts packets with the first byte in [128..191]
|
||||
// as defied in RFC7983
|
||||
func MatchSRTPOrSRTCP(b []byte) bool {
|
||||
return MatchRange(128, 191)(b)
|
||||
}
|
||||
|
||||
func isRTCP(buf []byte) bool {
|
||||
// Not long enough to determine RTP/RTCP
|
||||
if len(buf) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
var rtcpPacketType uint8
|
||||
r := bytes.NewReader([]byte{buf[1]})
|
||||
if err := binary.Read(r, binary.BigEndian, &rtcpPacketType); err != nil {
|
||||
return false
|
||||
} else if rtcpPacketType >= 192 && rtcpPacketType <= 223 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MatchSRTP is a MatchFunc that only matches SRTP and not SRTCP
|
||||
func MatchSRTP(buf []byte) bool {
|
||||
return MatchSRTPOrSRTCP(buf) && !isRTCP(buf)
|
||||
}
|
||||
|
||||
// MatchSRTCP is a MatchFunc that only matches SRTCP and not SRTP
|
||||
func MatchSRTCP(buf []byte) bool {
|
||||
return MatchSRTPOrSRTCP(buf) && isRTCP(buf)
|
||||
}
|
72
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/util/util.go
generated
vendored
Normal file
72
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/internal/util/util.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Package util provides auxiliary functions internally used in webrtc package
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/randutil"
|
||||
)
|
||||
|
||||
const (
|
||||
runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
)
|
||||
|
||||
// Use global random generator to properly seed by crypto grade random.
|
||||
var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
|
||||
|
||||
// MathRandAlpha generates a mathmatical random alphabet sequence of the requested length.
|
||||
func MathRandAlpha(n int) string {
|
||||
return globalMathRandomGenerator.GenerateString(n, runesAlpha)
|
||||
}
|
||||
|
||||
// RandUint32 generates a mathmatical random uint32.
|
||||
func RandUint32() uint32 {
|
||||
return globalMathRandomGenerator.Uint32()
|
||||
}
|
||||
|
||||
// FlattenErrs flattens multiple errors into one
|
||||
func FlattenErrs(errs []error) error {
|
||||
errs2 := []error{}
|
||||
for _, e := range errs {
|
||||
if e != nil {
|
||||
errs2 = append(errs2, e)
|
||||
}
|
||||
}
|
||||
if len(errs2) == 0 {
|
||||
return nil
|
||||
}
|
||||
return multiError(errs2)
|
||||
}
|
||||
|
||||
type multiError []error
|
||||
|
||||
func (me multiError) Error() string {
|
||||
var errstrings []string
|
||||
|
||||
for _, err := range me {
|
||||
if err != nil {
|
||||
errstrings = append(errstrings, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(errstrings) == 0 {
|
||||
return "multiError must contain multiple error but is empty"
|
||||
}
|
||||
|
||||
return strings.Join(errstrings, "\n")
|
||||
}
|
||||
|
||||
func (me multiError) Is(err error) bool {
|
||||
for _, e := range me {
|
||||
if errors.Is(e, err) {
|
||||
return true
|
||||
}
|
||||
if me2, ok := e.(multiError); ok {
|
||||
if me2.Is(err) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_compare.go
generated
vendored
Normal file
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_compare.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// +build js,go1.14
|
||||
|
||||
package webrtc
|
||||
|
||||
import "syscall/js"
|
||||
|
||||
func jsValueIsUndefined(v js.Value) bool {
|
||||
return v.IsUndefined()
|
||||
}
|
||||
|
||||
func jsValueIsNull(v js.Value) bool {
|
||||
return v.IsNull()
|
||||
}
|
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_compare_legacy.go
generated
vendored
Normal file
13
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_compare_legacy.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// +build js,!go1.14
|
||||
|
||||
package webrtc
|
||||
|
||||
import "syscall/js"
|
||||
|
||||
func jsValueIsUndefined(v js.Value) bool {
|
||||
return v == js.Undefined()
|
||||
}
|
||||
|
||||
func jsValueIsNull(v js.Value) bool {
|
||||
return v == js.Null()
|
||||
}
|
168
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_utils.go
generated
vendored
Normal file
168
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/js_utils.go
generated
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
// +build js,wasm
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// awaitPromise accepts a js.Value representing a Promise. If the promise
|
||||
// resolves, it returns (result, nil). If the promise rejects, it returns
|
||||
// (js.Undefined, error). awaitPromise has a synchronous-like API but does not
|
||||
// block the JavaScript event loop.
|
||||
func awaitPromise(promise js.Value) (js.Value, error) {
|
||||
resultsChan := make(chan js.Value)
|
||||
errChan := make(chan js.Error)
|
||||
|
||||
thenFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
resultsChan <- args[0]
|
||||
}()
|
||||
return js.Undefined()
|
||||
})
|
||||
defer thenFunc.Release()
|
||||
promise.Call("then", thenFunc)
|
||||
|
||||
catchFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go func() {
|
||||
errChan <- js.Error{args[0]}
|
||||
}()
|
||||
return js.Undefined()
|
||||
})
|
||||
defer catchFunc.Release()
|
||||
promise.Call("catch", catchFunc)
|
||||
|
||||
select {
|
||||
case result := <-resultsChan:
|
||||
return result, nil
|
||||
case err := <-errChan:
|
||||
return js.Undefined(), err
|
||||
}
|
||||
}
|
||||
|
||||
func valueToUint16Pointer(val js.Value) *uint16 {
|
||||
if jsValueIsNull(val) || jsValueIsUndefined(val) {
|
||||
return nil
|
||||
}
|
||||
convertedVal := uint16(val.Int())
|
||||
return &convertedVal
|
||||
}
|
||||
|
||||
func valueToStringPointer(val js.Value) *string {
|
||||
if jsValueIsNull(val) || jsValueIsUndefined(val) {
|
||||
return nil
|
||||
}
|
||||
stringVal := val.String()
|
||||
return &stringVal
|
||||
}
|
||||
|
||||
func stringToValueOrUndefined(val string) js.Value {
|
||||
if val == "" {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(val)
|
||||
}
|
||||
|
||||
func uint8ToValueOrUndefined(val uint8) js.Value {
|
||||
if val == 0 {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(val)
|
||||
}
|
||||
|
||||
func interfaceToValueOrUndefined(val interface{}) js.Value {
|
||||
if val == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(val)
|
||||
}
|
||||
|
||||
func valueToStringOrZero(val js.Value) string {
|
||||
if jsValueIsUndefined(val) || jsValueIsNull(val) {
|
||||
return ""
|
||||
}
|
||||
return val.String()
|
||||
}
|
||||
|
||||
func valueToUint8OrZero(val js.Value) uint8 {
|
||||
if jsValueIsUndefined(val) || jsValueIsNull(val) {
|
||||
return 0
|
||||
}
|
||||
return uint8(val.Int())
|
||||
}
|
||||
|
||||
func valueToUint16OrZero(val js.Value) uint16 {
|
||||
if jsValueIsNull(val) || jsValueIsUndefined(val) {
|
||||
return 0
|
||||
}
|
||||
return uint16(val.Int())
|
||||
}
|
||||
|
||||
func valueToUint32OrZero(val js.Value) uint32 {
|
||||
if jsValueIsNull(val) || jsValueIsUndefined(val) {
|
||||
return 0
|
||||
}
|
||||
return uint32(val.Int())
|
||||
}
|
||||
|
||||
func valueToStrings(val js.Value) []string {
|
||||
result := make([]string, val.Length())
|
||||
for i := 0; i < val.Length(); i++ {
|
||||
result[i] = val.Index(i).String()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func stringPointerToValue(val *string) js.Value {
|
||||
if val == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(*val)
|
||||
}
|
||||
|
||||
func uint16PointerToValue(val *uint16) js.Value {
|
||||
if val == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(*val)
|
||||
}
|
||||
|
||||
func boolPointerToValue(val *bool) js.Value {
|
||||
if val == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(*val)
|
||||
}
|
||||
|
||||
func stringsToValue(strings []string) js.Value {
|
||||
val := make([]interface{}, len(strings))
|
||||
for i, s := range strings {
|
||||
val[i] = s
|
||||
}
|
||||
return js.ValueOf(val)
|
||||
}
|
||||
|
||||
func stringEnumToValueOrUndefined(s string) js.Value {
|
||||
if s == "unknown" {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(s)
|
||||
}
|
||||
|
||||
// Converts the return value of recover() to an error.
|
||||
func recoveryToError(e interface{}) error {
|
||||
switch e := e.(type) {
|
||||
case error:
|
||||
return e
|
||||
default:
|
||||
return fmt.Errorf("recovered with non-error value: (%T) %s", e, e)
|
||||
}
|
||||
}
|
||||
|
||||
func uint8ArrayValueToBytes(val js.Value) []byte {
|
||||
result := make([]byte, val.Length())
|
||||
js.CopyBytesToGo(result, val)
|
||||
|
||||
return result
|
||||
}
|
512
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/mediaengine.go
generated
vendored
Normal file
512
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/mediaengine.go
generated
vendored
Normal file
|
@ -0,0 +1,512 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
"github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
// MimeTypeH264 H264 MIME type.
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypeH264 = "video/h264"
|
||||
// MimeTypeOpus Opus MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypeOpus = "audio/opus"
|
||||
// MimeTypeVP8 VP8 MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypeVP8 = "video/vp8"
|
||||
// MimeTypeVP9 VP9 MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypeVP9 = "video/vp9"
|
||||
// MimeTypeG722 G722 MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypeG722 = "audio/G722"
|
||||
// MimeTypePCMU PCMU MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypePCMU = "audio/PCMU"
|
||||
// MimeTypePCMA PCMA MIME type
|
||||
// Note: Matching should be case insensitive.
|
||||
MimeTypePCMA = "audio/PCMA"
|
||||
)
|
||||
|
||||
type mediaEngineHeaderExtension struct {
|
||||
uri string
|
||||
isAudio, isVideo bool
|
||||
|
||||
// If set only Transceivers of this direction are allowed
|
||||
allowedDirections []RTPTransceiverDirection
|
||||
}
|
||||
|
||||
// A MediaEngine defines the codecs supported by a PeerConnection, and the
|
||||
// configuration of those codecs. A MediaEngine must not be shared between
|
||||
// PeerConnections.
|
||||
type MediaEngine struct {
|
||||
// If we have attempted to negotiate a codec type yet.
|
||||
negotiatedVideo, negotiatedAudio bool
|
||||
|
||||
videoCodecs, audioCodecs []RTPCodecParameters
|
||||
negotiatedVideoCodecs, negotiatedAudioCodecs []RTPCodecParameters
|
||||
|
||||
headerExtensions []mediaEngineHeaderExtension
|
||||
negotiatedHeaderExtensions map[int]mediaEngineHeaderExtension
|
||||
}
|
||||
|
||||
// RegisterDefaultCodecs registers the default codecs supported by Pion WebRTC.
|
||||
// RegisterDefaultCodecs is not safe for concurrent use.
|
||||
func (m *MediaEngine) RegisterDefaultCodecs() error {
|
||||
// Default Pion Audio Codecs
|
||||
for _, codec := range []RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil},
|
||||
PayloadType: 111,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeG722, 8000, 0, "", nil},
|
||||
PayloadType: 9,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypePCMU, 8000, 0, "", nil},
|
||||
PayloadType: 0,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypePCMA, 8000, 0, "", nil},
|
||||
PayloadType: 8,
|
||||
},
|
||||
} {
|
||||
if err := m.RegisterCodec(codec, RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Default Pion Audio Header Extensions
|
||||
for _, extension := range []string{
|
||||
"urn:ietf:params:rtp-hdrext:sdes:mid",
|
||||
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
|
||||
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
|
||||
} {
|
||||
if err := m.RegisterHeaderExtension(RTPHeaderExtensionCapability{extension}, RTPCodecTypeAudio); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
videoRTCPFeedback := []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}}
|
||||
for _, codec := range []RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", videoRTCPFeedback},
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
|
||||
PayloadType: 97,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", videoRTCPFeedback},
|
||||
PayloadType: 98,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
|
||||
PayloadType: 99,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=1", videoRTCPFeedback},
|
||||
PayloadType: 100,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=100", nil},
|
||||
PayloadType: 101,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", videoRTCPFeedback},
|
||||
PayloadType: 102,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil},
|
||||
PayloadType: 121,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
|
||||
PayloadType: 127,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
|
||||
PayloadType: 120,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback},
|
||||
PayloadType: 125,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=125", nil},
|
||||
PayloadType: 107,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback},
|
||||
PayloadType: 108,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=108", nil},
|
||||
PayloadType: 109,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
|
||||
PayloadType: 127,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
|
||||
PayloadType: 120,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032", videoRTCPFeedback},
|
||||
PayloadType: 123,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=123", nil},
|
||||
PayloadType: 118,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/ulpfec", 90000, 0, "", nil},
|
||||
PayloadType: 116,
|
||||
},
|
||||
} {
|
||||
if err := m.RegisterCodec(codec, RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Default Pion Video Header Extensions
|
||||
for _, extension := range []string{
|
||||
"urn:ietf:params:rtp-hdrext:sdes:mid",
|
||||
"urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
|
||||
"urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
|
||||
} {
|
||||
if err := m.RegisterHeaderExtension(RTPHeaderExtensionCapability{extension}, RTPCodecTypeVideo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterCodec adds codec to the MediaEngine
|
||||
// These are the list of codecs supported by this PeerConnection.
|
||||
// RegisterCodec is not safe for concurrent use.
|
||||
func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType) error {
|
||||
codec.statsID = fmt.Sprintf("RTPCodec-%d", time.Now().UnixNano())
|
||||
switch typ {
|
||||
case RTPCodecTypeAudio:
|
||||
m.audioCodecs = append(m.audioCodecs, codec)
|
||||
case RTPCodecTypeVideo:
|
||||
m.videoCodecs = append(m.videoCodecs, codec)
|
||||
default:
|
||||
return ErrUnknownType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterHeaderExtension adds a header extension to the MediaEngine
|
||||
// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete
|
||||
func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapability, typ RTPCodecType, allowedDirections ...RTPTransceiverDirection) error {
|
||||
if m.negotiatedHeaderExtensions == nil {
|
||||
m.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
|
||||
}
|
||||
|
||||
if len(allowedDirections) == 0 {
|
||||
allowedDirections = []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly}
|
||||
}
|
||||
|
||||
for _, direction := range allowedDirections {
|
||||
if direction != RTPTransceiverDirectionRecvonly && direction != RTPTransceiverDirectionSendonly {
|
||||
return ErrRegisterHeaderExtensionInvalidDirection
|
||||
}
|
||||
}
|
||||
|
||||
extensionIndex := -1
|
||||
for i := range m.headerExtensions {
|
||||
if extension.URI == m.headerExtensions[i].uri {
|
||||
extensionIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
if extensionIndex == -1 {
|
||||
m.headerExtensions = append(m.headerExtensions, mediaEngineHeaderExtension{})
|
||||
extensionIndex = len(m.headerExtensions) - 1
|
||||
}
|
||||
|
||||
if typ == RTPCodecTypeAudio {
|
||||
m.headerExtensions[extensionIndex].isAudio = true
|
||||
} else if typ == RTPCodecTypeVideo {
|
||||
m.headerExtensions[extensionIndex].isVideo = true
|
||||
}
|
||||
|
||||
m.headerExtensions[extensionIndex].uri = extension.URI
|
||||
m.headerExtensions[extensionIndex].allowedDirections = allowedDirections
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterFeedback adds feedback mechanism to already registered codecs.
|
||||
func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType) {
|
||||
switch typ {
|
||||
case RTPCodecTypeVideo:
|
||||
for i, v := range m.videoCodecs {
|
||||
v.RTCPFeedback = append(v.RTCPFeedback, feedback)
|
||||
m.videoCodecs[i] = v
|
||||
}
|
||||
case RTPCodecTypeAudio:
|
||||
for i, v := range m.audioCodecs {
|
||||
v.RTCPFeedback = append(v.RTCPFeedback, feedback)
|
||||
m.audioCodecs[i] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getHeaderExtensionID returns the negotiated ID for a header extension.
|
||||
// If the Header Extension isn't enabled ok will be false
|
||||
func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) (val int, audioNegotiated, videoNegotiated bool) {
|
||||
if m.negotiatedHeaderExtensions == nil {
|
||||
return 0, false, false
|
||||
}
|
||||
|
||||
for id, h := range m.negotiatedHeaderExtensions {
|
||||
if extension.URI == h.uri {
|
||||
return id, h.isAudio, h.isVideo
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
|
||||
for _, codec := range m.negotiatedVideoCodecs {
|
||||
if codec.PayloadType == payloadType {
|
||||
return codec, RTPCodecTypeVideo, nil
|
||||
}
|
||||
}
|
||||
for _, codec := range m.negotiatedAudioCodecs {
|
||||
if codec.PayloadType == payloadType {
|
||||
return codec, RTPCodecTypeAudio, nil
|
||||
}
|
||||
}
|
||||
|
||||
return RTPCodecParameters{}, 0, ErrCodecNotFound
|
||||
}
|
||||
|
||||
func (m *MediaEngine) collectStats(collector *statsReportCollector) {
|
||||
statsLoop := func(codecs []RTPCodecParameters) {
|
||||
for _, codec := range codecs {
|
||||
collector.Collecting()
|
||||
stats := CodecStats{
|
||||
Timestamp: statsTimestampFrom(time.Now()),
|
||||
Type: StatsTypeCodec,
|
||||
ID: codec.statsID,
|
||||
PayloadType: codec.PayloadType,
|
||||
MimeType: codec.MimeType,
|
||||
ClockRate: codec.ClockRate,
|
||||
Channels: uint8(codec.Channels),
|
||||
SDPFmtpLine: codec.SDPFmtpLine,
|
||||
}
|
||||
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
}
|
||||
|
||||
statsLoop(m.videoCodecs)
|
||||
statsLoop(m.audioCodecs)
|
||||
}
|
||||
|
||||
// Look up a codec and enable if it exists
|
||||
func (m *MediaEngine) updateCodecParameters(remoteCodec RTPCodecParameters, typ RTPCodecType) error {
|
||||
codecs := m.videoCodecs
|
||||
if typ == RTPCodecTypeAudio {
|
||||
codecs = m.audioCodecs
|
||||
}
|
||||
|
||||
pushCodec := func(codec RTPCodecParameters) error {
|
||||
if typ == RTPCodecTypeAudio {
|
||||
m.negotiatedAudioCodecs = append(m.negotiatedAudioCodecs, codec)
|
||||
} else if typ == RTPCodecTypeVideo {
|
||||
m.negotiatedVideoCodecs = append(m.negotiatedVideoCodecs, codec)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt=") {
|
||||
payloadType, err := strconv.Atoi(strings.TrimPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt="))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err = m.getCodecByPayload(PayloadType(payloadType)); err != nil {
|
||||
return nil // not an error, we just ignore this codec we don't support
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := codecParametersFuzzySearch(remoteCodec, codecs); err == nil {
|
||||
return pushCodec(remoteCodec)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Look up a header extension and enable if it exists
|
||||
func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCodecType) error {
|
||||
if m.negotiatedHeaderExtensions == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, localExtension := range m.headerExtensions {
|
||||
if localExtension.uri == extension {
|
||||
h := mediaEngineHeaderExtension{uri: extension, allowedDirections: localExtension.allowedDirections}
|
||||
if existingValue, ok := m.negotiatedHeaderExtensions[id]; ok {
|
||||
h = existingValue
|
||||
}
|
||||
|
||||
switch {
|
||||
case localExtension.isAudio && typ == RTPCodecTypeAudio:
|
||||
h.isAudio = true
|
||||
case localExtension.isVideo && typ == RTPCodecTypeVideo:
|
||||
h.isVideo = true
|
||||
}
|
||||
|
||||
m.negotiatedHeaderExtensions[id] = h
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the MediaEngine from a remote description
|
||||
func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error {
|
||||
for _, media := range desc.MediaDescriptions {
|
||||
var typ RTPCodecType
|
||||
switch {
|
||||
case !m.negotiatedAudio && strings.EqualFold(media.MediaName.Media, "audio"):
|
||||
m.negotiatedAudio = true
|
||||
typ = RTPCodecTypeAudio
|
||||
case !m.negotiatedVideo && strings.EqualFold(media.MediaName.Media, "video"):
|
||||
m.negotiatedVideo = true
|
||||
typ = RTPCodecTypeVideo
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
codecs, err := codecsFromMediaDescription(media)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, codec := range codecs {
|
||||
if err = m.updateCodecParameters(codec, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
extensions, err := rtpExtensionsFromMediaDescription(media)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for extension, id := range extensions {
|
||||
if err = m.updateHeaderExtension(id, extension, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters {
|
||||
if typ == RTPCodecTypeVideo {
|
||||
if m.negotiatedVideo {
|
||||
return m.negotiatedVideoCodecs
|
||||
}
|
||||
|
||||
return m.videoCodecs
|
||||
} else if typ == RTPCodecTypeAudio {
|
||||
if m.negotiatedAudio {
|
||||
return m.negotiatedAudioCodecs
|
||||
}
|
||||
|
||||
return m.audioCodecs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters {
|
||||
headerExtensions := make([]RTPHeaderExtensionParameter, 0)
|
||||
|
||||
if m.negotiatedVideo && typ == RTPCodecTypeVideo ||
|
||||
m.negotiatedAudio && typ == RTPCodecTypeAudio {
|
||||
for id, e := range m.negotiatedHeaderExtensions {
|
||||
if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
|
||||
headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for id, e := range m.headerExtensions {
|
||||
if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
|
||||
headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id + 1, URI: e.uri})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RTPParameters{
|
||||
HeaderExtensions: headerExtensions,
|
||||
Codecs: m.getCodecsByKind(typ),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getRTPParametersByPayloadType(payloadType PayloadType) (RTPParameters, error) {
|
||||
codec, typ, err := m.getCodecByPayload(payloadType)
|
||||
if err != nil {
|
||||
return RTPParameters{}, err
|
||||
}
|
||||
|
||||
headerExtensions := make([]RTPHeaderExtensionParameter, 0)
|
||||
for id, e := range m.negotiatedHeaderExtensions {
|
||||
if e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo {
|
||||
headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
|
||||
}
|
||||
}
|
||||
|
||||
return RTPParameters{
|
||||
HeaderExtensions: headerExtensions,
|
||||
Codecs: []RTPCodecParameters{codec},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) {
|
||||
switch strings.ToLower(codec.MimeType) {
|
||||
case strings.ToLower(MimeTypeH264):
|
||||
return &codecs.H264Payloader{}, nil
|
||||
case strings.ToLower(MimeTypeOpus):
|
||||
return &codecs.OpusPayloader{}, nil
|
||||
case strings.ToLower(MimeTypeVP8):
|
||||
return &codecs.VP8Payloader{}, nil
|
||||
case strings.ToLower(MimeTypeVP9):
|
||||
return &codecs.VP9Payloader{}, nil
|
||||
case strings.ToLower(MimeTypeG722):
|
||||
return &codecs.G722Payloader{}, nil
|
||||
case strings.ToLower(MimeTypePCMU), strings.ToLower(MimeTypePCMA):
|
||||
return &codecs.G711Payloader{}, nil
|
||||
default:
|
||||
return nil, ErrNoPayloaderForCodec
|
||||
}
|
||||
}
|
104
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/networktype.go
generated
vendored
Normal file
104
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/networktype.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
)
|
||||
|
||||
func supportedNetworkTypes() []NetworkType {
|
||||
return []NetworkType{
|
||||
NetworkTypeUDP4,
|
||||
NetworkTypeUDP6,
|
||||
// NetworkTypeTCP4, // Not supported yet
|
||||
// NetworkTypeTCP6, // Not supported yet
|
||||
}
|
||||
}
|
||||
|
||||
// NetworkType represents the type of network
|
||||
type NetworkType int
|
||||
|
||||
const (
|
||||
// NetworkTypeUDP4 indicates UDP over IPv4.
|
||||
NetworkTypeUDP4 NetworkType = iota + 1
|
||||
|
||||
// NetworkTypeUDP6 indicates UDP over IPv6.
|
||||
NetworkTypeUDP6
|
||||
|
||||
// NetworkTypeTCP4 indicates TCP over IPv4.
|
||||
NetworkTypeTCP4
|
||||
|
||||
// NetworkTypeTCP6 indicates TCP over IPv6.
|
||||
NetworkTypeTCP6
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
networkTypeUDP4Str = "udp4"
|
||||
networkTypeUDP6Str = "udp6"
|
||||
networkTypeTCP4Str = "tcp4"
|
||||
networkTypeTCP6Str = "tcp6"
|
||||
)
|
||||
|
||||
func (t NetworkType) String() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4:
|
||||
return networkTypeUDP4Str
|
||||
case NetworkTypeUDP6:
|
||||
return networkTypeUDP6Str
|
||||
case NetworkTypeTCP4:
|
||||
return networkTypeTCP4Str
|
||||
case NetworkTypeTCP6:
|
||||
return networkTypeTCP6Str
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// Protocol returns udp or tcp
|
||||
func (t NetworkType) Protocol() string {
|
||||
switch t {
|
||||
case NetworkTypeUDP4:
|
||||
return "udp"
|
||||
case NetworkTypeUDP6:
|
||||
return "udp"
|
||||
case NetworkTypeTCP4:
|
||||
return "tcp"
|
||||
case NetworkTypeTCP6:
|
||||
return "tcp"
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// NewNetworkType allows create network type from string
|
||||
// It will be useful for getting custom network types from external config.
|
||||
func NewNetworkType(raw string) (NetworkType, error) {
|
||||
switch raw {
|
||||
case networkTypeUDP4Str:
|
||||
return NetworkTypeUDP4, nil
|
||||
case networkTypeUDP6Str:
|
||||
return NetworkTypeUDP6, nil
|
||||
case networkTypeTCP4Str:
|
||||
return NetworkTypeTCP4, nil
|
||||
case networkTypeTCP6Str:
|
||||
return NetworkTypeTCP6, nil
|
||||
default:
|
||||
return NetworkType(Unknown), fmt.Errorf("%w: %s", errNetworkTypeUnknown, raw)
|
||||
}
|
||||
}
|
||||
|
||||
func getNetworkType(iceNetworkType ice.NetworkType) (NetworkType, error) {
|
||||
switch iceNetworkType {
|
||||
case ice.NetworkTypeUDP4:
|
||||
return NetworkTypeUDP4, nil
|
||||
case ice.NetworkTypeUDP6:
|
||||
return NetworkTypeUDP6, nil
|
||||
case ice.NetworkTypeTCP4:
|
||||
return NetworkTypeTCP4, nil
|
||||
case ice.NetworkTypeTCP6:
|
||||
return NetworkTypeTCP6, nil
|
||||
default:
|
||||
return NetworkType(Unknown), fmt.Errorf("%w: %s", errNetworkTypeUnknown, iceNetworkType.String())
|
||||
}
|
||||
}
|
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/oauthcredential.go
generated
vendored
Normal file
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/oauthcredential.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
package webrtc
|
||||
|
||||
// OAuthCredential represents OAuth credential information which is used by
|
||||
// the STUN/TURN client to connect to an ICE server as defined in
|
||||
// https://tools.ietf.org/html/rfc7635. Note that the kid parameter is not
|
||||
// located in OAuthCredential, but in ICEServer's username member.
|
||||
type OAuthCredential struct {
|
||||
// MACKey is a base64-url encoded format. It is used in STUN message
|
||||
// integrity hash calculation.
|
||||
MACKey string
|
||||
|
||||
// AccessToken is a base64-encoded format. This is an encrypted
|
||||
// self-contained token that is opaque to the application.
|
||||
AccessToken string
|
||||
}
|
26
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/offeransweroptions.go
generated
vendored
Normal file
26
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/offeransweroptions.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
package webrtc
|
||||
|
||||
// OfferAnswerOptions is a base structure which describes the options that
|
||||
// can be used to control the offer/answer creation process.
|
||||
type OfferAnswerOptions struct {
|
||||
// VoiceActivityDetection allows the application to provide information
|
||||
// about whether it wishes voice detection feature to be enabled or disabled.
|
||||
VoiceActivityDetection bool
|
||||
}
|
||||
|
||||
// AnswerOptions structure describes the options used to control the answer
|
||||
// creation process.
|
||||
type AnswerOptions struct {
|
||||
OfferAnswerOptions
|
||||
}
|
||||
|
||||
// OfferOptions structure describes the options used to control the offer
|
||||
// creation process
|
||||
type OfferOptions struct {
|
||||
OfferAnswerOptions
|
||||
|
||||
// ICERestart forces the underlying ice gathering process to be restarted.
|
||||
// When this value is true, the generated description will have ICE
|
||||
// credentials that are different from the current credentials
|
||||
ICERestart bool
|
||||
}
|
87
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/operations.go
generated
vendored
Normal file
87
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/operations.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Operation is a function
|
||||
type operation func()
|
||||
|
||||
// Operations is a task executor.
|
||||
type operations struct {
|
||||
mu sync.Mutex
|
||||
busy bool
|
||||
ops []operation
|
||||
}
|
||||
|
||||
func newOperations() *operations {
|
||||
return &operations{}
|
||||
}
|
||||
|
||||
// Enqueue adds a new action to be executed. If there are no actions scheduled,
|
||||
// the execution will start immediately in a new goroutine.
|
||||
func (o *operations) Enqueue(op operation) {
|
||||
if op == nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.mu.Lock()
|
||||
running := o.busy
|
||||
o.ops = append(o.ops, op)
|
||||
o.busy = true
|
||||
o.mu.Unlock()
|
||||
|
||||
if !running {
|
||||
go o.start()
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty checks if there are tasks in the queue
|
||||
func (o *operations) IsEmpty() bool {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
return len(o.ops) == 0
|
||||
}
|
||||
|
||||
// Done blocks until all currently enqueued operations are finished executing.
|
||||
// For more complex synchronization, use Enqueue directly.
|
||||
func (o *operations) Done() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
o.Enqueue(func() {
|
||||
wg.Done()
|
||||
})
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (o *operations) pop() func() {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
if len(o.ops) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fn := o.ops[0]
|
||||
o.ops = o.ops[1:]
|
||||
return fn
|
||||
}
|
||||
|
||||
func (o *operations) start() {
|
||||
defer func() {
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
if len(o.ops) == 0 {
|
||||
o.busy = false
|
||||
return
|
||||
}
|
||||
// either a new operation was enqueued while we
|
||||
// were busy, or an operation panicked
|
||||
go o.start()
|
||||
}()
|
||||
|
||||
fn := o.pop()
|
||||
for fn != nil {
|
||||
fn()
|
||||
fn = o.pop()
|
||||
}
|
||||
}
|
11
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/package.json
generated
vendored
Normal file
11
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/package.json
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "webrtc",
|
||||
"repository": "git@github.com:pion/webrtc.git",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"wrtc": "0.4.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"request": "2.88.2"
|
||||
}
|
||||
}
|
2294
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnection.go
generated
vendored
Normal file
2294
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnection.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
676
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnection_js.go
generated
vendored
Normal file
676
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnection_js.go
generated
vendored
Normal file
|
@ -0,0 +1,676 @@
|
|||
// +build js,wasm
|
||||
|
||||
// Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document.
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"syscall/js"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
// PeerConnection represents a WebRTC connection that establishes a
|
||||
// peer-to-peer communications with another PeerConnection instance in a
|
||||
// browser, or to another endpoint implementing the required protocols.
|
||||
type PeerConnection struct {
|
||||
// Pointer to the underlying JavaScript RTCPeerConnection object.
|
||||
underlying js.Value
|
||||
|
||||
// Keep track of handlers/callbacks so we can call Release as required by the
|
||||
// syscall/js API. Initially nil.
|
||||
onSignalingStateChangeHandler *js.Func
|
||||
onDataChannelHandler *js.Func
|
||||
onNegotiationNeededHandler *js.Func
|
||||
onConnectionStateChangeHandler *js.Func
|
||||
onICEConnectionStateChangeHandler *js.Func
|
||||
onICECandidateHandler *js.Func
|
||||
onICEGatheringStateChangeHandler *js.Func
|
||||
|
||||
// Used by GatheringCompletePromise
|
||||
onGatherCompleteHandler func()
|
||||
|
||||
// A reference to the associated API state used by this connection
|
||||
api *API
|
||||
}
|
||||
|
||||
// NewPeerConnection creates a peerconnection.
|
||||
func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
|
||||
api := NewAPI()
|
||||
return api.NewPeerConnection(configuration)
|
||||
}
|
||||
|
||||
// NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
|
||||
func (api *API) NewPeerConnection(configuration Configuration) (_ *PeerConnection, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
configMap := configurationToValue(configuration)
|
||||
underlying := js.Global().Get("window").Get("RTCPeerConnection").New(configMap)
|
||||
return &PeerConnection{
|
||||
underlying: underlying,
|
||||
api: api,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) JSValue() js.Value {
|
||||
return pc.underlying
|
||||
}
|
||||
|
||||
// OnSignalingStateChange sets an event handler which is invoked when the
|
||||
// peer connection's signaling state changes
|
||||
func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) {
|
||||
if pc.onSignalingStateChangeHandler != nil {
|
||||
oldHandler := pc.onSignalingStateChangeHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
state := newSignalingState(args[0].String())
|
||||
go f(state)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onSignalingStateChangeHandler = &onSignalingStateChangeHandler
|
||||
pc.underlying.Set("onsignalingstatechange", onSignalingStateChangeHandler)
|
||||
}
|
||||
|
||||
// OnDataChannel sets an event handler which is invoked when a data
|
||||
// channel message arrives from a remote peer.
|
||||
func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
|
||||
if pc.onDataChannelHandler != nil {
|
||||
oldHandler := pc.onDataChannelHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
// pion/webrtc/projects/15
|
||||
// This reference to the underlying DataChannel doesn't know
|
||||
// about any other references to the same DataChannel. This might result in
|
||||
// memory leaks where we don't clean up handler functions. Could possibly fix
|
||||
// by keeping a mutex-protected list of all DataChannel references as a
|
||||
// property of this PeerConnection, but at the cost of additional overhead.
|
||||
dataChannel := &DataChannel{
|
||||
underlying: args[0].Get("channel"),
|
||||
api: pc.api,
|
||||
}
|
||||
go f(dataChannel)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onDataChannelHandler = &onDataChannelHandler
|
||||
pc.underlying.Set("ondatachannel", onDataChannelHandler)
|
||||
}
|
||||
|
||||
// OnNegotiationNeeded sets an event handler which is invoked when
|
||||
// a change has occurred which requires session negotiation
|
||||
func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
|
||||
if pc.onNegotiationNeededHandler != nil {
|
||||
oldHandler := pc.onNegotiationNeededHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go f()
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onNegotiationNeededHandler = &onNegotiationNeededHandler
|
||||
pc.underlying.Set("onnegotiationneeded", onNegotiationNeededHandler)
|
||||
}
|
||||
|
||||
// OnICEConnectionStateChange sets an event handler which is called
|
||||
// when an ICE connection state is changed.
|
||||
func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
|
||||
if pc.onICEConnectionStateChangeHandler != nil {
|
||||
oldHandler := pc.onICEConnectionStateChangeHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
connectionState := NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
|
||||
go f(connectionState)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onICEConnectionStateChangeHandler = &onICEConnectionStateChangeHandler
|
||||
pc.underlying.Set("oniceconnectionstatechange", onICEConnectionStateChangeHandler)
|
||||
}
|
||||
|
||||
// OnConnectionStateChange sets an event handler which is called
|
||||
// when an PeerConnectionState is changed.
|
||||
func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
|
||||
if pc.onConnectionStateChangeHandler != nil {
|
||||
oldHandler := pc.onConnectionStateChangeHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
connectionState := newPeerConnectionState(pc.underlying.Get("connectionState").String())
|
||||
go f(connectionState)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onConnectionStateChangeHandler = &onConnectionStateChangeHandler
|
||||
pc.underlying.Set("onconnectionstatechange", onConnectionStateChangeHandler)
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) checkConfiguration(configuration Configuration) error {
|
||||
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
|
||||
if pc.ConnectionState() == PeerConnectionStateClosed {
|
||||
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
|
||||
}
|
||||
|
||||
existingConfig := pc.GetConfiguration()
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
|
||||
if configuration.PeerIdentity != "" {
|
||||
if configuration.PeerIdentity != existingConfig.PeerIdentity {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/pion/webrtc/issues/513
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
|
||||
// if len(configuration.Certificates) > 0 {
|
||||
// if len(configuration.Certificates) != len(existingConfiguration.Certificates) {
|
||||
// return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
|
||||
// }
|
||||
|
||||
// for i, certificate := range configuration.Certificates {
|
||||
// if !pc.configuration.Certificates[i].Equals(certificate) {
|
||||
// return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
|
||||
// }
|
||||
// }
|
||||
// pc.configuration.Certificates = configuration.Certificates
|
||||
// }
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
|
||||
if configuration.BundlePolicy != BundlePolicy(Unknown) {
|
||||
if configuration.BundlePolicy != existingConfig.BundlePolicy {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
|
||||
if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
|
||||
if configuration.RTCPMuxPolicy != existingConfig.RTCPMuxPolicy {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
|
||||
if configuration.ICECandidatePoolSize != 0 {
|
||||
if configuration.ICECandidatePoolSize != existingConfig.ICECandidatePoolSize &&
|
||||
pc.LocalDescription() != nil {
|
||||
return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
|
||||
if len(configuration.ICEServers) > 0 {
|
||||
// https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
|
||||
for _, server := range configuration.ICEServers {
|
||||
if _, err := server.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetConfiguration updates the configuration of this PeerConnection object.
|
||||
func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
if err := pc.checkConfiguration(configuration); err != nil {
|
||||
return err
|
||||
}
|
||||
configMap := configurationToValue(configuration)
|
||||
pc.underlying.Call("setConfiguration", configMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfiguration returns a Configuration object representing the current
|
||||
// configuration of this PeerConnection object. The returned object is a
|
||||
// copy and direct mutation on it will not take affect until SetConfiguration
|
||||
// has been called with Configuration passed as its only argument.
|
||||
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration
|
||||
func (pc *PeerConnection) GetConfiguration() Configuration {
|
||||
return valueToConfiguration(pc.underlying.Call("getConfiguration"))
|
||||
}
|
||||
|
||||
// CreateOffer starts the PeerConnection and generates the localDescription
|
||||
func (pc *PeerConnection) CreateOffer(options *OfferOptions) (_ SessionDescription, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
promise := pc.underlying.Call("createOffer", offerOptionsToValue(options))
|
||||
desc, err := awaitPromise(promise)
|
||||
if err != nil {
|
||||
return SessionDescription{}, err
|
||||
}
|
||||
return *valueToSessionDescription(desc), nil
|
||||
}
|
||||
|
||||
// CreateAnswer starts the PeerConnection and generates the localDescription
|
||||
func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (_ SessionDescription, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
promise := pc.underlying.Call("createAnswer", answerOptionsToValue(options))
|
||||
desc, err := awaitPromise(promise)
|
||||
if err != nil {
|
||||
return SessionDescription{}, err
|
||||
}
|
||||
return *valueToSessionDescription(desc), nil
|
||||
}
|
||||
|
||||
// SetLocalDescription sets the SessionDescription of the local peer
|
||||
func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
promise := pc.underlying.Call("setLocalDescription", sessionDescriptionToValue(&desc))
|
||||
_, err = awaitPromise(promise)
|
||||
return err
|
||||
}
|
||||
|
||||
// LocalDescription returns PendingLocalDescription if it is not null and
|
||||
// otherwise it returns CurrentLocalDescription. This property is used to
|
||||
// determine if setLocalDescription has already been called.
|
||||
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription
|
||||
func (pc *PeerConnection) LocalDescription() *SessionDescription {
|
||||
return valueToSessionDescription(pc.underlying.Get("localDescription"))
|
||||
}
|
||||
|
||||
// SetRemoteDescription sets the SessionDescription of the remote peer
|
||||
func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
promise := pc.underlying.Call("setRemoteDescription", sessionDescriptionToValue(&desc))
|
||||
_, err = awaitPromise(promise)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoteDescription returns PendingRemoteDescription if it is not null and
|
||||
// otherwise it returns CurrentRemoteDescription. This property is used to
|
||||
// determine if setRemoteDescription has already been called.
|
||||
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription
|
||||
func (pc *PeerConnection) RemoteDescription() *SessionDescription {
|
||||
return valueToSessionDescription(pc.underlying.Get("remoteDescription"))
|
||||
}
|
||||
|
||||
// AddICECandidate accepts an ICE candidate string and adds it
|
||||
// to the existing set of candidates
|
||||
func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
promise := pc.underlying.Call("addIceCandidate", iceCandidateInitToValue(candidate))
|
||||
_, err = awaitPromise(promise)
|
||||
return err
|
||||
}
|
||||
|
||||
// ICEConnectionState returns the ICE connection state of the
|
||||
// PeerConnection instance.
|
||||
func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
|
||||
return NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
|
||||
}
|
||||
|
||||
// OnICECandidate sets an event handler which is invoked when a new ICE
|
||||
// candidate is found.
|
||||
func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) {
|
||||
if pc.onICECandidateHandler != nil {
|
||||
oldHandler := pc.onICECandidateHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
candidate := valueToICECandidate(args[0].Get("candidate"))
|
||||
if candidate == nil && pc.onGatherCompleteHandler != nil {
|
||||
go pc.onGatherCompleteHandler()
|
||||
}
|
||||
|
||||
go f(candidate)
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onICECandidateHandler = &onICECandidateHandler
|
||||
pc.underlying.Set("onicecandidate", onICECandidateHandler)
|
||||
}
|
||||
|
||||
// OnICEGatheringStateChange sets an event handler which is invoked when the
|
||||
// ICE candidate gathering state has changed.
|
||||
func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
|
||||
if pc.onICEGatheringStateChangeHandler != nil {
|
||||
oldHandler := pc.onICEGatheringStateChangeHandler
|
||||
defer oldHandler.Release()
|
||||
}
|
||||
onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
go f()
|
||||
return js.Undefined()
|
||||
})
|
||||
pc.onICEGatheringStateChangeHandler = &onICEGatheringStateChangeHandler
|
||||
pc.underlying.Set("onicegatheringstatechange", onICEGatheringStateChangeHandler)
|
||||
}
|
||||
|
||||
// CreateDataChannel creates a new DataChannel object with the given label
|
||||
// and optional DataChannelInit used to configure properties of the
|
||||
// underlying channel such as data reliability.
|
||||
func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (_ *DataChannel, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
channel := pc.underlying.Call("createDataChannel", label, dataChannelInitToValue(options))
|
||||
return &DataChannel{
|
||||
underlying: channel,
|
||||
api: pc.api,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetIdentityProvider is used to configure an identity provider to generate identity assertions
|
||||
func (pc *PeerConnection) SetIdentityProvider(provider string) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
pc.underlying.Call("setIdentityProvider", provider)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close ends the PeerConnection
|
||||
func (pc *PeerConnection) Close() (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = recoveryToError(e)
|
||||
}
|
||||
}()
|
||||
|
||||
pc.underlying.Call("close")
|
||||
|
||||
// Release any handlers as required by the syscall/js API.
|
||||
if pc.onSignalingStateChangeHandler != nil {
|
||||
pc.onSignalingStateChangeHandler.Release()
|
||||
}
|
||||
if pc.onDataChannelHandler != nil {
|
||||
pc.onDataChannelHandler.Release()
|
||||
}
|
||||
if pc.onNegotiationNeededHandler != nil {
|
||||
pc.onNegotiationNeededHandler.Release()
|
||||
}
|
||||
if pc.onConnectionStateChangeHandler != nil {
|
||||
pc.onConnectionStateChangeHandler.Release()
|
||||
}
|
||||
if pc.onICEConnectionStateChangeHandler != nil {
|
||||
pc.onICEConnectionStateChangeHandler.Release()
|
||||
}
|
||||
if pc.onICECandidateHandler != nil {
|
||||
pc.onICECandidateHandler.Release()
|
||||
}
|
||||
if pc.onICEGatheringStateChangeHandler != nil {
|
||||
pc.onICEGatheringStateChangeHandler.Release()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentLocalDescription represents the local description that was
|
||||
// successfully negotiated the last time the PeerConnection transitioned
|
||||
// into the stable state plus any local candidates that have been generated
|
||||
// by the ICEAgent since the offer or answer was created.
|
||||
func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
|
||||
desc := pc.underlying.Get("currentLocalDescription")
|
||||
return valueToSessionDescription(desc)
|
||||
}
|
||||
|
||||
// PendingLocalDescription represents a local description that is in the
|
||||
// process of being negotiated plus any local candidates that have been
|
||||
// generated by the ICEAgent since the offer or answer was created. If the
|
||||
// PeerConnection is in the stable state, the value is null.
|
||||
func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
|
||||
desc := pc.underlying.Get("pendingLocalDescription")
|
||||
return valueToSessionDescription(desc)
|
||||
}
|
||||
|
||||
// CurrentRemoteDescription represents the last remote description that was
|
||||
// successfully negotiated the last time the PeerConnection transitioned
|
||||
// into the stable state plus any remote candidates that have been supplied
|
||||
// via AddICECandidate() since the offer or answer was created.
|
||||
func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescription {
|
||||
desc := pc.underlying.Get("currentRemoteDescription")
|
||||
return valueToSessionDescription(desc)
|
||||
}
|
||||
|
||||
// PendingRemoteDescription represents a remote description that is in the
|
||||
// process of being negotiated, complete with any remote candidates that
|
||||
// have been supplied via AddICECandidate() since the offer or answer was
|
||||
// created. If the PeerConnection is in the stable state, the value is
|
||||
// null.
|
||||
func (pc *PeerConnection) PendingRemoteDescription() *SessionDescription {
|
||||
desc := pc.underlying.Get("pendingRemoteDescription")
|
||||
return valueToSessionDescription(desc)
|
||||
}
|
||||
|
||||
// SignalingState returns the signaling state of the PeerConnection instance.
|
||||
func (pc *PeerConnection) SignalingState() SignalingState {
|
||||
rawState := pc.underlying.Get("signalingState").String()
|
||||
return newSignalingState(rawState)
|
||||
}
|
||||
|
||||
// ICEGatheringState attribute the ICE gathering state of the PeerConnection
|
||||
// instance.
|
||||
func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
|
||||
rawState := pc.underlying.Get("iceGatheringState").String()
|
||||
return NewICEGatheringState(rawState)
|
||||
}
|
||||
|
||||
// ConnectionState attribute the connection state of the PeerConnection
|
||||
// instance.
|
||||
func (pc *PeerConnection) ConnectionState() PeerConnectionState {
|
||||
rawState := pc.underlying.Get("connectionState").String()
|
||||
return newPeerConnectionState(rawState)
|
||||
}
|
||||
|
||||
func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
|
||||
pc.onGatherCompleteHandler = handler
|
||||
|
||||
// If no onIceCandidate handler has been set provide an empty one
|
||||
// otherwise our onGatherCompleteHandler will not be executed
|
||||
if pc.onICECandidateHandler == nil {
|
||||
pc.OnICECandidate(func(i *ICECandidate) {})
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a Configuration to js.Value so it can be passed
|
||||
// through to the JavaScript WebRTC API. Any zero values are converted to
|
||||
// js.Undefined(), which will result in the default value being used.
|
||||
func configurationToValue(configuration Configuration) js.Value {
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"iceServers": iceServersToValue(configuration.ICEServers),
|
||||
"iceTransportPolicy": stringEnumToValueOrUndefined(configuration.ICETransportPolicy.String()),
|
||||
"bundlePolicy": stringEnumToValueOrUndefined(configuration.BundlePolicy.String()),
|
||||
"rtcpMuxPolicy": stringEnumToValueOrUndefined(configuration.RTCPMuxPolicy.String()),
|
||||
"peerIdentity": stringToValueOrUndefined(configuration.PeerIdentity),
|
||||
"iceCandidatePoolSize": uint8ToValueOrUndefined(configuration.ICECandidatePoolSize),
|
||||
|
||||
// Note: Certificates are not currently supported.
|
||||
// "certificates": configuration.Certificates,
|
||||
})
|
||||
}
|
||||
|
||||
func iceServersToValue(iceServers []ICEServer) js.Value {
|
||||
if len(iceServers) == 0 {
|
||||
return js.Undefined()
|
||||
}
|
||||
maps := make([]interface{}, len(iceServers))
|
||||
for i, server := range iceServers {
|
||||
maps[i] = iceServerToValue(server)
|
||||
}
|
||||
return js.ValueOf(maps)
|
||||
}
|
||||
|
||||
func iceServerToValue(server ICEServer) js.Value {
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"urls": stringsToValue(server.URLs), // required
|
||||
"username": stringToValueOrUndefined(server.Username),
|
||||
// Note: credential and credentialType are not currently supported.
|
||||
// "credential": interfaceToValueOrUndefined(server.Credential),
|
||||
// "credentialType": stringEnumToValueOrUndefined(server.CredentialType.String()),
|
||||
})
|
||||
}
|
||||
|
||||
func valueToConfiguration(configValue js.Value) Configuration {
|
||||
if jsValueIsNull(configValue) || jsValueIsUndefined(configValue) {
|
||||
return Configuration{}
|
||||
}
|
||||
return Configuration{
|
||||
ICEServers: valueToICEServers(configValue.Get("iceServers")),
|
||||
ICETransportPolicy: NewICETransportPolicy(valueToStringOrZero(configValue.Get("iceTransportPolicy"))),
|
||||
BundlePolicy: newBundlePolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
|
||||
RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("rtcpMuxPolicy"))),
|
||||
PeerIdentity: valueToStringOrZero(configValue.Get("peerIdentity")),
|
||||
ICECandidatePoolSize: valueToUint8OrZero(configValue.Get("iceCandidatePoolSize")),
|
||||
|
||||
// Note: Certificates are not supported.
|
||||
// Certificates []Certificate
|
||||
}
|
||||
}
|
||||
|
||||
func valueToICEServers(iceServersValue js.Value) []ICEServer {
|
||||
if jsValueIsNull(iceServersValue) || jsValueIsUndefined(iceServersValue) {
|
||||
return nil
|
||||
}
|
||||
iceServers := make([]ICEServer, iceServersValue.Length())
|
||||
for i := 0; i < iceServersValue.Length(); i++ {
|
||||
iceServers[i] = valueToICEServer(iceServersValue.Index(i))
|
||||
}
|
||||
return iceServers
|
||||
}
|
||||
|
||||
func valueToICEServer(iceServerValue js.Value) ICEServer {
|
||||
return ICEServer{
|
||||
URLs: valueToStrings(iceServerValue.Get("urls")), // required
|
||||
Username: valueToStringOrZero(iceServerValue.Get("username")),
|
||||
// Note: Credential and CredentialType are not currently supported.
|
||||
// Credential: iceServerValue.Get("credential"),
|
||||
// CredentialType: newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))),
|
||||
}
|
||||
}
|
||||
|
||||
func valueToICECandidate(val js.Value) *ICECandidate {
|
||||
if jsValueIsNull(val) || jsValueIsUndefined(val) {
|
||||
return nil
|
||||
}
|
||||
if jsValueIsUndefined(val.Get("protocol")) && !jsValueIsUndefined(val.Get("candidate")) {
|
||||
// Missing some fields, assume it's Firefox and parse SDP candidate.
|
||||
c, err := ice.UnmarshalCandidate(val.Get("candidate").String())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
iceCandidate, err := newICECandidateFromICE(c)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &iceCandidate
|
||||
}
|
||||
protocol, _ := NewICEProtocol(val.Get("protocol").String())
|
||||
candidateType, _ := NewICECandidateType(val.Get("type").String())
|
||||
return &ICECandidate{
|
||||
Foundation: val.Get("foundation").String(),
|
||||
Priority: valueToUint32OrZero(val.Get("priority")),
|
||||
Address: val.Get("address").String(),
|
||||
Protocol: protocol,
|
||||
Port: valueToUint16OrZero(val.Get("port")),
|
||||
Typ: candidateType,
|
||||
Component: stringToComponentIDOrZero(val.Get("component").String()),
|
||||
RelatedAddress: val.Get("relatedAddress").String(),
|
||||
RelatedPort: valueToUint16OrZero(val.Get("relatedPort")),
|
||||
}
|
||||
}
|
||||
|
||||
func stringToComponentIDOrZero(val string) uint16 {
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceComponent
|
||||
switch val {
|
||||
case "rtp":
|
||||
return 1
|
||||
case "rtcp":
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func sessionDescriptionToValue(desc *SessionDescription) js.Value {
|
||||
if desc == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"type": desc.Type.String(),
|
||||
"sdp": desc.SDP,
|
||||
})
|
||||
}
|
||||
|
||||
func valueToSessionDescription(descValue js.Value) *SessionDescription {
|
||||
if jsValueIsNull(descValue) || jsValueIsUndefined(descValue) {
|
||||
return nil
|
||||
}
|
||||
return &SessionDescription{
|
||||
Type: NewSDPType(descValue.Get("type").String()),
|
||||
SDP: descValue.Get("sdp").String(),
|
||||
}
|
||||
}
|
||||
|
||||
func offerOptionsToValue(offerOptions *OfferOptions) js.Value {
|
||||
if offerOptions == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"iceRestart": offerOptions.ICERestart,
|
||||
"voiceActivityDetection": offerOptions.VoiceActivityDetection,
|
||||
})
|
||||
}
|
||||
|
||||
func answerOptionsToValue(answerOptions *AnswerOptions) js.Value {
|
||||
if answerOptions == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"voiceActivityDetection": answerOptions.VoiceActivityDetection,
|
||||
})
|
||||
}
|
||||
|
||||
func iceCandidateInitToValue(candidate ICECandidateInit) js.Value {
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"candidate": candidate.Candidate,
|
||||
"sdpMid": stringPointerToValue(candidate.SDPMid),
|
||||
"sdpMLineIndex": uint16PointerToValue(candidate.SDPMLineIndex),
|
||||
"usernameFragment": stringPointerToValue(candidate.UsernameFragment),
|
||||
})
|
||||
}
|
||||
|
||||
func dataChannelInitToValue(options *DataChannelInit) js.Value {
|
||||
if options == nil {
|
||||
return js.Undefined()
|
||||
}
|
||||
|
||||
maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime)
|
||||
return js.ValueOf(map[string]interface{}{
|
||||
"ordered": boolPointerToValue(options.Ordered),
|
||||
"maxPacketLifeTime": maxPacketLifeTime,
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
|
||||
// Chrome calls this "maxRetransmitTime"
|
||||
"maxRetransmitTime": maxPacketLifeTime,
|
||||
"maxRetransmits": uint16PointerToValue(options.MaxRetransmits),
|
||||
"protocol": stringPointerToValue(options.Protocol),
|
||||
"negotiated": boolPointerToValue(options.Negotiated),
|
||||
"id": uint16PointerToValue(options.ID),
|
||||
})
|
||||
}
|
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go
generated
vendored
Normal file
94
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package webrtc
|
||||
|
||||
// PeerConnectionState indicates the state of the PeerConnection.
|
||||
type PeerConnectionState int
|
||||
|
||||
const (
|
||||
// PeerConnectionStateNew indicates that any of the ICETransports or
|
||||
// DTLSTransports are in the "new" state and none of the transports are
|
||||
// in the "connecting", "checking", "failed" or "disconnected" state, or
|
||||
// all transports are in the "closed" state, or there are no transports.
|
||||
PeerConnectionStateNew PeerConnectionState = iota + 1
|
||||
|
||||
// PeerConnectionStateConnecting indicates that any of the
|
||||
// ICETransports or DTLSTransports are in the "connecting" or
|
||||
// "checking" state and none of them is in the "failed" state.
|
||||
PeerConnectionStateConnecting
|
||||
|
||||
// PeerConnectionStateConnected indicates that all ICETransports and
|
||||
// DTLSTransports are in the "connected", "completed" or "closed" state
|
||||
// and at least one of them is in the "connected" or "completed" state.
|
||||
PeerConnectionStateConnected
|
||||
|
||||
// PeerConnectionStateDisconnected indicates that any of the
|
||||
// ICETransports or DTLSTransports are in the "disconnected" state
|
||||
// and none of them are in the "failed" or "connecting" or "checking" state.
|
||||
PeerConnectionStateDisconnected
|
||||
|
||||
// PeerConnectionStateFailed indicates that any of the ICETransports
|
||||
// or DTLSTransports are in a "failed" state.
|
||||
PeerConnectionStateFailed
|
||||
|
||||
// PeerConnectionStateClosed indicates the peer connection is closed
|
||||
// and the isClosed member variable of PeerConnection is true.
|
||||
PeerConnectionStateClosed
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
peerConnectionStateNewStr = "new"
|
||||
peerConnectionStateConnectingStr = "connecting"
|
||||
peerConnectionStateConnectedStr = "connected"
|
||||
peerConnectionStateDisconnectedStr = "disconnected"
|
||||
peerConnectionStateFailedStr = "failed"
|
||||
peerConnectionStateClosedStr = "closed"
|
||||
)
|
||||
|
||||
func newPeerConnectionState(raw string) PeerConnectionState {
|
||||
switch raw {
|
||||
case peerConnectionStateNewStr:
|
||||
return PeerConnectionStateNew
|
||||
case peerConnectionStateConnectingStr:
|
||||
return PeerConnectionStateConnecting
|
||||
case peerConnectionStateConnectedStr:
|
||||
return PeerConnectionStateConnected
|
||||
case peerConnectionStateDisconnectedStr:
|
||||
return PeerConnectionStateDisconnected
|
||||
case peerConnectionStateFailedStr:
|
||||
return PeerConnectionStateFailed
|
||||
case peerConnectionStateClosedStr:
|
||||
return PeerConnectionStateClosed
|
||||
default:
|
||||
return PeerConnectionState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t PeerConnectionState) String() string {
|
||||
switch t {
|
||||
case PeerConnectionStateNew:
|
||||
return peerConnectionStateNewStr
|
||||
case PeerConnectionStateConnecting:
|
||||
return peerConnectionStateConnectingStr
|
||||
case PeerConnectionStateConnected:
|
||||
return peerConnectionStateConnectedStr
|
||||
case PeerConnectionStateDisconnected:
|
||||
return peerConnectionStateDisconnectedStr
|
||||
case PeerConnectionStateFailed:
|
||||
return peerConnectionStateFailedStr
|
||||
case PeerConnectionStateClosed:
|
||||
return peerConnectionStateClosedStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
type negotiationNeededState int
|
||||
|
||||
const (
|
||||
// NegotiationNeededStateEmpty not running and queue is empty
|
||||
negotiationNeededStateEmpty = iota
|
||||
// NegotiationNeededStateEmpty running and queue is empty
|
||||
negotiationNeededStateRun
|
||||
// NegotiationNeededStateEmpty running and queue
|
||||
negotiationNeededStateQueue
|
||||
)
|
191
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264reader/h264reader.go
generated
vendored
Normal file
191
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264reader/h264reader.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Package h264reader implements a H264 Annex-B Reader
|
||||
package h264reader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// H264Reader reads data from stream and constructs h264 nal units
|
||||
type H264Reader struct {
|
||||
stream io.Reader
|
||||
nalBuffer []byte
|
||||
countOfConsecutiveZeroBytes int
|
||||
nalPrefixParsed bool
|
||||
readBuffer []byte
|
||||
}
|
||||
|
||||
var (
|
||||
errNilReader = errors.New("stream is nil")
|
||||
errDataIsNotH264Stream = errors.New("data is not a H264 bitstream")
|
||||
)
|
||||
|
||||
// NewReader creates new H264Reader
|
||||
func NewReader(in io.Reader) (*H264Reader, error) {
|
||||
if in == nil {
|
||||
return nil, errNilReader
|
||||
}
|
||||
|
||||
reader := &H264Reader{
|
||||
stream: in,
|
||||
nalBuffer: make([]byte, 0),
|
||||
nalPrefixParsed: false,
|
||||
readBuffer: make([]byte, 0),
|
||||
}
|
||||
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
// NAL H.264 Network Abstraction Layer
|
||||
type NAL struct {
|
||||
PictureOrderCount uint32
|
||||
|
||||
// NAL header
|
||||
ForbiddenZeroBit bool
|
||||
RefIdc uint8
|
||||
UnitType NalUnitType
|
||||
|
||||
Data []byte // header byte + rbsp
|
||||
}
|
||||
|
||||
func (reader *H264Reader) read(numToRead int) (data []byte) {
|
||||
for len(reader.readBuffer) < numToRead {
|
||||
buf := make([]byte, 4096)
|
||||
n, err := reader.stream.Read(buf)
|
||||
if n == 0 || err != nil {
|
||||
break
|
||||
}
|
||||
buf = buf[0:n]
|
||||
reader.readBuffer = append(reader.readBuffer, buf...)
|
||||
}
|
||||
var numShouldRead int
|
||||
if numToRead <= len(reader.readBuffer) {
|
||||
numShouldRead = numToRead
|
||||
} else {
|
||||
numShouldRead = len(reader.readBuffer)
|
||||
}
|
||||
data = reader.readBuffer[0:numShouldRead]
|
||||
reader.readBuffer = reader.readBuffer[numShouldRead:]
|
||||
return data
|
||||
}
|
||||
|
||||
func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLength int, e error) {
|
||||
nalPrefix3Bytes := []byte{0, 0, 1}
|
||||
nalPrefix4Bytes := []byte{0, 0, 0, 1}
|
||||
|
||||
prefixBuffer := reader.read(4)
|
||||
|
||||
n := len(prefixBuffer)
|
||||
|
||||
if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if n < 3 {
|
||||
return 0, errDataIsNotH264Stream
|
||||
}
|
||||
|
||||
nalPrefix3BytesFound := bytes.Equal(nalPrefix3Bytes, prefixBuffer[:3])
|
||||
if n == 3 {
|
||||
if nalPrefix3BytesFound {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, errDataIsNotH264Stream
|
||||
}
|
||||
|
||||
// n == 4
|
||||
if nalPrefix3BytesFound {
|
||||
reader.nalBuffer = append(reader.nalBuffer, prefixBuffer[3])
|
||||
return 3, nil
|
||||
}
|
||||
|
||||
nalPrefix4BytesFound := bytes.Equal(nalPrefix4Bytes, prefixBuffer)
|
||||
if nalPrefix4BytesFound {
|
||||
return 4, nil
|
||||
}
|
||||
return 0, errDataIsNotH264Stream
|
||||
}
|
||||
|
||||
// NextNAL reads from stream and returns then next NAL,
|
||||
// and an error if there is incomplete frame data.
|
||||
// Returns all nil values when no more NALs are available.
|
||||
func (reader *H264Reader) NextNAL() (*NAL, error) {
|
||||
if !reader.nalPrefixParsed {
|
||||
_, err := reader.bitStreamStartsWithH264Prefix()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader.nalPrefixParsed = true
|
||||
}
|
||||
|
||||
for {
|
||||
buffer := reader.read(1)
|
||||
n := len(buffer)
|
||||
|
||||
if n != 1 {
|
||||
break
|
||||
}
|
||||
readByte := buffer[0]
|
||||
nalFound := reader.processByte(readByte)
|
||||
if nalFound {
|
||||
nal := newNal(reader.nalBuffer)
|
||||
nal.parseHeader()
|
||||
if nal.UnitType == NalUnitTypeSEI {
|
||||
reader.nalBuffer = nil
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
reader.nalBuffer = append(reader.nalBuffer, readByte)
|
||||
}
|
||||
|
||||
if len(reader.nalBuffer) == 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
nal := newNal(reader.nalBuffer)
|
||||
reader.nalBuffer = nil
|
||||
nal.parseHeader()
|
||||
|
||||
return nal, nil
|
||||
}
|
||||
|
||||
func (reader *H264Reader) processByte(readByte byte) (nalFound bool) {
|
||||
nalFound = false
|
||||
|
||||
switch readByte {
|
||||
case 0:
|
||||
reader.countOfConsecutiveZeroBytes++
|
||||
case 1:
|
||||
if reader.countOfConsecutiveZeroBytes >= 2 {
|
||||
countOfConsecutiveZeroBytesInPrefix := 2
|
||||
if reader.countOfConsecutiveZeroBytes > 2 {
|
||||
countOfConsecutiveZeroBytesInPrefix = 3
|
||||
}
|
||||
nalUnitLength := len(reader.nalBuffer) - countOfConsecutiveZeroBytesInPrefix
|
||||
reader.nalBuffer = reader.nalBuffer[0:nalUnitLength]
|
||||
nalFound = true
|
||||
} else {
|
||||
reader.countOfConsecutiveZeroBytes = 0
|
||||
}
|
||||
default:
|
||||
reader.countOfConsecutiveZeroBytes = 0
|
||||
}
|
||||
|
||||
return nalFound
|
||||
}
|
||||
|
||||
func newNal(data []byte) *NAL {
|
||||
return &NAL{PictureOrderCount: 0, ForbiddenZeroBit: false, RefIdc: 0, UnitType: NalUnitTypeUnspecified, Data: data}
|
||||
}
|
||||
|
||||
func (h *NAL) parseHeader() {
|
||||
firstByte := h.Data[0]
|
||||
h.ForbiddenZeroBit = (((firstByte & 0x80) >> 7) == 1) // 0x80 = 0b10000000
|
||||
h.RefIdc = (firstByte & 0x60) >> 5 // 0x60 = 0b01100000
|
||||
h.UnitType = NalUnitType((firstByte & 0x1F) >> 0) // 0x1F = 0b00011111
|
||||
}
|
68
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264reader/nalunittype.go
generated
vendored
Normal file
68
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264reader/nalunittype.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package h264reader
|
||||
|
||||
import "strconv"
|
||||
|
||||
// NalUnitType is the type of a NAL
|
||||
type NalUnitType uint8
|
||||
|
||||
// Enums for NalUnitTypes
|
||||
const (
|
||||
NalUnitTypeUnspecified NalUnitType = 0 // Unspecified
|
||||
NalUnitTypeCodedSliceNonIdr NalUnitType = 1 // Coded slice of a non-IDR picture
|
||||
NalUnitTypeCodedSliceDataPartitionA NalUnitType = 2 // Coded slice data partition A
|
||||
NalUnitTypeCodedSliceDataPartitionB NalUnitType = 3 // Coded slice data partition B
|
||||
NalUnitTypeCodedSliceDataPartitionC NalUnitType = 4 // Coded slice data partition C
|
||||
NalUnitTypeCodedSliceIdr NalUnitType = 5 // Coded slice of an IDR picture
|
||||
NalUnitTypeSEI NalUnitType = 6 // Supplemental enhancement information (SEI)
|
||||
NalUnitTypeSPS NalUnitType = 7 // Sequence parameter set
|
||||
NalUnitTypePPS NalUnitType = 8 // Picture parameter set
|
||||
NalUnitTypeAUD NalUnitType = 9 // Access unit delimiter
|
||||
NalUnitTypeEndOfSequence NalUnitType = 10 // End of sequence
|
||||
NalUnitTypeEndOfStream NalUnitType = 11 // End of stream
|
||||
NalUnitTypeFiller NalUnitType = 12 // Filler data
|
||||
NalUnitTypeSpsExt NalUnitType = 13 // Sequence parameter set extension
|
||||
NalUnitTypeCodedSliceAux NalUnitType = 19 // Coded slice of an auxiliary coded picture without partitioning
|
||||
// 14..18 // Reserved
|
||||
// 20..23 // Reserved
|
||||
// 24..31 // Unspecified
|
||||
)
|
||||
|
||||
func (n *NalUnitType) String() string {
|
||||
var str string
|
||||
switch *n {
|
||||
case NalUnitTypeUnspecified:
|
||||
str = "Unspecified"
|
||||
case NalUnitTypeCodedSliceNonIdr:
|
||||
str = "CodedSliceNonIdr"
|
||||
case NalUnitTypeCodedSliceDataPartitionA:
|
||||
str = "CodedSliceDataPartitionA"
|
||||
case NalUnitTypeCodedSliceDataPartitionB:
|
||||
str = "CodedSliceDataPartitionB"
|
||||
case NalUnitTypeCodedSliceDataPartitionC:
|
||||
str = "CodedSliceDataPartitionC"
|
||||
case NalUnitTypeCodedSliceIdr:
|
||||
str = "CodedSliceIdr"
|
||||
case NalUnitTypeSEI:
|
||||
str = "SEI"
|
||||
case NalUnitTypeSPS:
|
||||
str = "SPS"
|
||||
case NalUnitTypePPS:
|
||||
str = "PPS"
|
||||
case NalUnitTypeAUD:
|
||||
str = "AUD"
|
||||
case NalUnitTypeEndOfSequence:
|
||||
str = "EndOfSequence"
|
||||
case NalUnitTypeEndOfStream:
|
||||
str = "EndOfStream"
|
||||
case NalUnitTypeFiller:
|
||||
str = "Filler"
|
||||
case NalUnitTypeSpsExt:
|
||||
str = "SpsExt"
|
||||
case NalUnitTypeCodedSliceAux:
|
||||
str = "NalUnitTypeCodedSliceAux"
|
||||
default:
|
||||
str = "Unknown"
|
||||
}
|
||||
str = str + "(" + strconv.FormatInt(int64(*n), 10) + ")"
|
||||
return str
|
||||
}
|
90
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264writer/h264writer.go
generated
vendored
Normal file
90
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/h264writer/h264writer.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Package h264writer implements H264 media container writer
|
||||
package h264writer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
)
|
||||
|
||||
type (
|
||||
// H264Writer is used to take RTP packets, parse them and
|
||||
// write the data to an io.Writer.
|
||||
// Currently it only supports non-interleaved mode
|
||||
// Therefore, only 1-23, 24 (STAP-A), 28 (FU-A) NAL types are allowed.
|
||||
// https://tools.ietf.org/html/rfc6184#section-5.2
|
||||
H264Writer struct {
|
||||
writer io.Writer
|
||||
hasKeyFrame bool
|
||||
}
|
||||
)
|
||||
|
||||
// New builds a new H264 writer
|
||||
func New(filename string) (*H264Writer, error) {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewWith(f), nil
|
||||
}
|
||||
|
||||
// NewWith initializes a new H264 writer with an io.Writer output
|
||||
func NewWith(w io.Writer) *H264Writer {
|
||||
return &H264Writer{
|
||||
writer: w,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteRTP adds a new packet and writes the appropriate headers for it
|
||||
func (h *H264Writer) WriteRTP(packet *rtp.Packet) error {
|
||||
if len(packet.Payload) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !h.hasKeyFrame {
|
||||
if h.hasKeyFrame = isKeyFrame(packet.Payload); !h.hasKeyFrame {
|
||||
// key frame not defined yet. discarding packet
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
data, err := (&codecs.H264Packet{}).Unmarshal(packet.Payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = h.writer.Write(data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Close closes the underlying writer
|
||||
func (h *H264Writer) Close() error {
|
||||
if h.writer != nil {
|
||||
if closer, ok := h.writer.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isKeyFrame(data []byte) bool {
|
||||
const typeSTAPA = 24
|
||||
|
||||
var word uint32
|
||||
|
||||
payload := bytes.NewReader(data)
|
||||
err := binary.Read(payload, binary.BigEndian, &word)
|
||||
|
||||
if err != nil || (word&0x1F000000)>>24 != typeSTAPA {
|
||||
return false
|
||||
}
|
||||
|
||||
return word&0x1F == 7
|
||||
}
|
147
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/ivfwriter/ivfwriter.go
generated
vendored
Normal file
147
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/ivfwriter/ivfwriter.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
// Package ivfwriter implements IVF media container writer
|
||||
package ivfwriter
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
)
|
||||
|
||||
var (
|
||||
errFileNotOpened = errors.New("file not opened")
|
||||
errInvalidNilPacket = errors.New("invalid nil packet")
|
||||
)
|
||||
|
||||
// IVFWriter is used to take RTP packets and write them to an IVF on disk
|
||||
type IVFWriter struct {
|
||||
ioWriter io.Writer
|
||||
count uint64
|
||||
seenKeyFrame bool
|
||||
currentFrame []byte
|
||||
}
|
||||
|
||||
// New builds a new IVF writer
|
||||
func New(fileName string) (*IVFWriter, error) {
|
||||
f, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer, err := NewWith(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer.ioWriter = f
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
// NewWith initialize a new IVF writer with an io.Writer output
|
||||
func NewWith(out io.Writer) (*IVFWriter, error) {
|
||||
if out == nil {
|
||||
return nil, errFileNotOpened
|
||||
}
|
||||
|
||||
writer := &IVFWriter{
|
||||
ioWriter: out,
|
||||
seenKeyFrame: false,
|
||||
}
|
||||
if err := writer.writeHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
func (i *IVFWriter) writeHeader() error {
|
||||
header := make([]byte, 32)
|
||||
copy(header[0:], "DKIF") // DKIF
|
||||
binary.LittleEndian.PutUint16(header[4:], 0) // Version
|
||||
binary.LittleEndian.PutUint16(header[6:], 32) // Header size
|
||||
copy(header[8:], "VP80") // FOURCC
|
||||
binary.LittleEndian.PutUint16(header[12:], 640) // Width in pixels
|
||||
binary.LittleEndian.PutUint16(header[14:], 480) // Height in pixels
|
||||
binary.LittleEndian.PutUint32(header[16:], 30) // Framerate denominator
|
||||
binary.LittleEndian.PutUint32(header[20:], 1) // Framerate numerator
|
||||
binary.LittleEndian.PutUint32(header[24:], 900) // Frame count, will be updated on first Close() call
|
||||
binary.LittleEndian.PutUint32(header[28:], 0) // Unused
|
||||
|
||||
_, err := i.ioWriter.Write(header)
|
||||
return err
|
||||
}
|
||||
|
||||
// WriteRTP adds a new packet and writes the appropriate headers for it
|
||||
func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error {
|
||||
if i.ioWriter == nil {
|
||||
return errFileNotOpened
|
||||
}
|
||||
|
||||
vp8Packet := codecs.VP8Packet{}
|
||||
if _, err := vp8Packet.Unmarshal(packet.Payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isKeyFrame := vp8Packet.Payload[0] & 0x01
|
||||
switch {
|
||||
case !i.seenKeyFrame && isKeyFrame == 1:
|
||||
return nil
|
||||
case i.currentFrame == nil && vp8Packet.S != 1:
|
||||
return nil
|
||||
}
|
||||
|
||||
i.seenKeyFrame = true
|
||||
i.currentFrame = append(i.currentFrame, vp8Packet.Payload[0:]...)
|
||||
|
||||
if !packet.Marker {
|
||||
return nil
|
||||
} else if len(i.currentFrame) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
frameHeader := make([]byte, 12)
|
||||
binary.LittleEndian.PutUint32(frameHeader[0:], uint32(len(i.currentFrame))) // Frame length
|
||||
binary.LittleEndian.PutUint64(frameHeader[4:], i.count) // PTS
|
||||
|
||||
i.count++
|
||||
|
||||
if _, err := i.ioWriter.Write(frameHeader); err != nil {
|
||||
return err
|
||||
} else if _, err := i.ioWriter.Write(i.currentFrame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.currentFrame = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close stops the recording
|
||||
func (i *IVFWriter) Close() error {
|
||||
if i.ioWriter == nil {
|
||||
// Returns no error as it may be convenient to call
|
||||
// Close() multiple times
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
i.ioWriter = nil
|
||||
}()
|
||||
|
||||
if ws, ok := i.ioWriter.(io.WriteSeeker); ok {
|
||||
// Update the framecount
|
||||
if _, err := ws.Seek(24, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
buff := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buff, uint32(i.count))
|
||||
if _, err := ws.Write(buff); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if closer, ok := i.ioWriter.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
25
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/media.go
generated
vendored
Normal file
25
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/media.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Package media provides media writer and filters
|
||||
package media
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// A Sample contains encoded media and timing information
|
||||
type Sample struct {
|
||||
Data []byte
|
||||
Timestamp time.Time
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// Writer defines an interface to handle
|
||||
// the creation of media files
|
||||
type Writer interface {
|
||||
// Add the content of an RTP packet to the media
|
||||
WriteRTP(packet *rtp.Packet) error
|
||||
// Close the media
|
||||
// Note: Close implementation must be idempotent
|
||||
Close() error
|
||||
}
|
215
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/oggreader/oggreader.go
generated
vendored
Normal file
215
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/oggreader/oggreader.go
generated
vendored
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Package oggreader implements the Ogg media container reader
|
||||
package oggreader
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
pageHeaderTypeBeginningOfStream = 0x02
|
||||
pageHeaderSignature = "OggS"
|
||||
|
||||
idPageSignature = "OpusHead"
|
||||
|
||||
pageHeaderLen = 27
|
||||
idPagePayloadLength = 19
|
||||
)
|
||||
|
||||
var (
|
||||
errNilStream = errors.New("stream is nil")
|
||||
errBadIDPageSignature = errors.New("bad header signature")
|
||||
errBadIDPageType = errors.New("wrong header, expected beginning of stream")
|
||||
errBadIDPageLength = errors.New("payload for id page must be 19 bytes")
|
||||
errBadIDPagePayloadSignature = errors.New("bad payload signature")
|
||||
errShortPageHeader = errors.New("not enough data for payload header")
|
||||
errChecksumMismatch = errors.New("expected and actual checksum do not match")
|
||||
)
|
||||
|
||||
// OggReader is used to read Ogg files and return page payloads
|
||||
type OggReader struct {
|
||||
stream io.ReadSeeker
|
||||
bytesReadSuccesfully int64
|
||||
checksumTable *[256]uint32
|
||||
doChecksum bool
|
||||
}
|
||||
|
||||
// OggHeader is the metadata from the first two pages
|
||||
// in the file (ID and Comment)
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7845.html#section-3
|
||||
type OggHeader struct {
|
||||
ChannelMap uint8
|
||||
Channels uint8
|
||||
OutputGain uint16
|
||||
PreSkip uint16
|
||||
SampleRate uint32
|
||||
Version uint8
|
||||
}
|
||||
|
||||
// OggPageHeader is the metadata for a Page
|
||||
// Pages are the fundamental unit of multiplexing in an Ogg stream
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7845.html#section-1
|
||||
type OggPageHeader struct {
|
||||
GranulePosition uint64
|
||||
|
||||
sig [4]byte
|
||||
version uint8
|
||||
headerType uint8
|
||||
serial uint32
|
||||
index uint32
|
||||
segmentsCount uint8
|
||||
}
|
||||
|
||||
// NewWith returns a new Ogg reader and Ogg header
|
||||
// with an io.ReadSeeker input
|
||||
func NewWith(in io.ReadSeeker) (*OggReader, *OggHeader, error) {
|
||||
return newWith(in /* doChecksum */, true)
|
||||
}
|
||||
|
||||
func newWith(in io.ReadSeeker, doChecksum bool) (*OggReader, *OggHeader, error) {
|
||||
if in == nil {
|
||||
return nil, nil, errNilStream
|
||||
}
|
||||
|
||||
reader := &OggReader{
|
||||
stream: in,
|
||||
checksumTable: generateChecksumTable(),
|
||||
doChecksum: doChecksum,
|
||||
}
|
||||
|
||||
header, err := reader.readHeaders()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return reader, header, nil
|
||||
}
|
||||
|
||||
func (o *OggReader) readHeaders() (*OggHeader, error) {
|
||||
payload, pageHeader, err := o.ParseNextPage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := &OggHeader{}
|
||||
if string(pageHeader.sig[:]) != pageHeaderSignature {
|
||||
return nil, errBadIDPageSignature
|
||||
}
|
||||
|
||||
if pageHeader.headerType != pageHeaderTypeBeginningOfStream {
|
||||
return nil, errBadIDPageType
|
||||
}
|
||||
|
||||
if len(payload) != idPagePayloadLength {
|
||||
return nil, errBadIDPageLength
|
||||
}
|
||||
|
||||
if s := string(payload[:8]); s != idPageSignature {
|
||||
return nil, errBadIDPagePayloadSignature
|
||||
}
|
||||
|
||||
header.Version = payload[8]
|
||||
header.Channels = payload[9]
|
||||
header.PreSkip = binary.LittleEndian.Uint16(payload[10:12])
|
||||
header.SampleRate = binary.LittleEndian.Uint32(payload[12:16])
|
||||
header.OutputGain = binary.LittleEndian.Uint16(payload[16:18])
|
||||
header.ChannelMap = payload[18]
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
// ParseNextPage reads from stream and returns Ogg page payload, header,
|
||||
// and an error if there is incomplete page data.
|
||||
func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) {
|
||||
h := make([]byte, pageHeaderLen)
|
||||
|
||||
n, err := io.ReadFull(o.stream, h)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if n < len(h) {
|
||||
return nil, nil, errShortPageHeader
|
||||
}
|
||||
|
||||
pageHeader := &OggPageHeader{
|
||||
sig: [4]byte{h[0], h[1], h[2], h[3]},
|
||||
}
|
||||
|
||||
pageHeader.version = h[4]
|
||||
pageHeader.headerType = h[5]
|
||||
pageHeader.GranulePosition = binary.LittleEndian.Uint64(h[6 : 6+8])
|
||||
pageHeader.serial = binary.LittleEndian.Uint32(h[14 : 14+4])
|
||||
pageHeader.index = binary.LittleEndian.Uint32(h[18 : 18+4])
|
||||
pageHeader.segmentsCount = h[26]
|
||||
|
||||
sizeBuffer := make([]byte, pageHeader.segmentsCount)
|
||||
if _, err = io.ReadFull(o.stream, sizeBuffer); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
payloadSize := 0
|
||||
for _, s := range sizeBuffer {
|
||||
payloadSize += int(s)
|
||||
}
|
||||
|
||||
payload := make([]byte, payloadSize)
|
||||
if _, err = io.ReadFull(o.stream, payload); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if o.doChecksum {
|
||||
var checksum uint32
|
||||
updateChecksum := func(v byte) {
|
||||
checksum = (checksum << 8) ^ o.checksumTable[byte(checksum>>24)^v]
|
||||
}
|
||||
|
||||
for index := range h {
|
||||
// Don't include expected checksum in our generation
|
||||
if index > 21 && index < 26 {
|
||||
updateChecksum(0)
|
||||
continue
|
||||
}
|
||||
|
||||
updateChecksum(h[index])
|
||||
}
|
||||
for _, s := range sizeBuffer {
|
||||
updateChecksum(s)
|
||||
}
|
||||
for index := range payload {
|
||||
updateChecksum(payload[index])
|
||||
}
|
||||
|
||||
if binary.LittleEndian.Uint32(h[22:22+4]) != checksum {
|
||||
return nil, nil, errChecksumMismatch
|
||||
}
|
||||
}
|
||||
|
||||
return payload, pageHeader, nil
|
||||
}
|
||||
|
||||
// ResetReader resets the internal stream of OggReader. This is useful
|
||||
// for live streams, where the end of the file might be read without the
|
||||
// data being finished.
|
||||
func (o *OggReader) ResetReader(reset func(bytesRead int64) io.ReadSeeker) {
|
||||
o.stream = reset(o.bytesReadSuccesfully)
|
||||
}
|
||||
|
||||
func generateChecksumTable() *[256]uint32 {
|
||||
var table [256]uint32
|
||||
const poly = 0x04c11db7
|
||||
|
||||
for i := range table {
|
||||
r := uint32(i) << 24
|
||||
for j := 0; j < 8; j++ {
|
||||
if (r & 0x80000000) != 0 {
|
||||
r = (r << 1) ^ poly
|
||||
} else {
|
||||
r <<= 1
|
||||
}
|
||||
table[i] = (r & 0xffffffff)
|
||||
}
|
||||
}
|
||||
return &table
|
||||
}
|
261
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/oggwriter/oggwriter.go
generated
vendored
Normal file
261
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/media/oggwriter/oggwriter.go
generated
vendored
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Package oggwriter implements OGG media container writer
|
||||
package oggwriter
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pion/randutil"
|
||||
"github.com/pion/rtp"
|
||||
"github.com/pion/rtp/codecs"
|
||||
)
|
||||
|
||||
const (
|
||||
pageHeaderTypeContinuationOfStream = 0x00
|
||||
pageHeaderTypeBeginningOfStream = 0x02
|
||||
pageHeaderTypeEndOfStream = 0x04
|
||||
defaultPreSkip = 3840 // 3840 recommended in the RFC
|
||||
idPageSignature = "OpusHead"
|
||||
commentPageSignature = "OpusTags"
|
||||
pageHeaderSignature = "OggS"
|
||||
)
|
||||
|
||||
var (
|
||||
errFileNotOpened = errors.New("file not opened")
|
||||
errInvalidNilPacket = errors.New("invalid nil packet")
|
||||
)
|
||||
|
||||
// OggWriter is used to take RTP packets and write them to an OGG on disk
|
||||
type OggWriter struct {
|
||||
stream io.Writer
|
||||
fd *os.File
|
||||
sampleRate uint32
|
||||
channelCount uint16
|
||||
serial uint32
|
||||
pageIndex uint32
|
||||
checksumTable *[256]uint32
|
||||
previousGranulePosition uint64
|
||||
previousTimestamp uint32
|
||||
lastPayloadSize int
|
||||
}
|
||||
|
||||
// New builds a new OGG Opus writer
|
||||
func New(fileName string, sampleRate uint32, channelCount uint16) (*OggWriter, error) {
|
||||
f, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer, err := NewWith(f, sampleRate, channelCount)
|
||||
if err != nil {
|
||||
return nil, f.Close()
|
||||
}
|
||||
writer.fd = f
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
// NewWith initialize a new OGG Opus writer with an io.Writer output
|
||||
func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*OggWriter, error) {
|
||||
if out == nil {
|
||||
return nil, errFileNotOpened
|
||||
}
|
||||
|
||||
writer := &OggWriter{
|
||||
stream: out,
|
||||
sampleRate: sampleRate,
|
||||
channelCount: channelCount,
|
||||
serial: randutil.NewMathRandomGenerator().Uint32(),
|
||||
checksumTable: generateChecksumTable(),
|
||||
|
||||
// Timestamp and Granule MUST start from 1
|
||||
// Only headers can have 0 values
|
||||
previousTimestamp: 1,
|
||||
previousGranulePosition: 1,
|
||||
}
|
||||
if err := writer.writeHeaders(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
/*
|
||||
ref: https://tools.ietf.org/html/rfc7845.html
|
||||
https://git.xiph.org/?p=opus-tools.git;a=blob;f=src/opus_header.c#l219
|
||||
|
||||
Page 0 Pages 1 ... n Pages (n+1) ...
|
||||
+------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
|
||||
| | | | | | | | | | | | |
|
||||
|+----------+| |+-----------------+| |+-------------------+ +-----
|
||||
|||ID Header|| || Comment Header || ||Audio Data Packet 1| | ...
|
||||
|+----------+| |+-----------------+| |+-------------------+ +-----
|
||||
| | | | | | | | | | | | |
|
||||
+------------+ +---+ +---+ ... +---+ +-----------+ +---------+ +--
|
||||
^ ^ ^
|
||||
| | |
|
||||
| | Mandatory Page Break
|
||||
| |
|
||||
| ID header is contained on a single page
|
||||
|
|
||||
'Beginning Of Stream'
|
||||
|
||||
Figure 1: Example Packet Organization for a Logical Ogg Opus Stream
|
||||
*/
|
||||
|
||||
func (i *OggWriter) writeHeaders() error {
|
||||
// ID Header
|
||||
oggIDHeader := make([]byte, 19)
|
||||
|
||||
copy(oggIDHeader[0:], idPageSignature) // Magic Signature 'OpusHead'
|
||||
oggIDHeader[8] = 1 // Version
|
||||
oggIDHeader[9] = uint8(i.channelCount) // Channel count
|
||||
binary.LittleEndian.PutUint16(oggIDHeader[10:], defaultPreSkip) // pre-skip
|
||||
binary.LittleEndian.PutUint32(oggIDHeader[12:], i.sampleRate) // original sample rate, any valid sample e.g 48000
|
||||
binary.LittleEndian.PutUint16(oggIDHeader[16:], 0) // output gain
|
||||
oggIDHeader[18] = 0 // channel map 0 = one stream: mono or stereo
|
||||
|
||||
// Reference: https://tools.ietf.org/html/rfc7845.html#page-6
|
||||
// RFC specifies that the ID Header page should have a granule position of 0 and a Header Type set to 2 (StartOfStream)
|
||||
data := i.createPage(oggIDHeader, pageHeaderTypeBeginningOfStream, 0, i.pageIndex)
|
||||
if err := i.writeToStream(data); err != nil {
|
||||
return err
|
||||
}
|
||||
i.pageIndex++
|
||||
|
||||
// Comment Header
|
||||
oggCommentHeader := make([]byte, 21)
|
||||
copy(oggCommentHeader[0:], commentPageSignature) // Magic Signature 'OpusTags'
|
||||
binary.LittleEndian.PutUint32(oggCommentHeader[8:], 5) // Vendor Length
|
||||
copy(oggCommentHeader[12:], "pion") // Vendor name 'pion'
|
||||
binary.LittleEndian.PutUint32(oggCommentHeader[17:], 0) // User Comment List Length
|
||||
|
||||
// RFC specifies that the page where the CommentHeader completes should have a granule position of 0
|
||||
data = i.createPage(oggCommentHeader, pageHeaderTypeContinuationOfStream, 0, i.pageIndex)
|
||||
if err := i.writeToStream(data); err != nil {
|
||||
return err
|
||||
}
|
||||
i.pageIndex++
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
pageHeaderSize = 27
|
||||
)
|
||||
|
||||
func (i *OggWriter) createPage(payload []uint8, headerType uint8, granulePos uint64, pageIndex uint32) []byte {
|
||||
i.lastPayloadSize = len(payload)
|
||||
page := make([]byte, pageHeaderSize+1+i.lastPayloadSize)
|
||||
|
||||
copy(page[0:], pageHeaderSignature) // page headers starts with 'OggS'
|
||||
page[4] = 0 // Version
|
||||
page[5] = headerType // 1 = continuation, 2 = beginning of stream, 4 = end of stream
|
||||
binary.LittleEndian.PutUint64(page[6:], granulePos) // granule position
|
||||
binary.LittleEndian.PutUint32(page[14:], i.serial) // Bitstream serial number
|
||||
binary.LittleEndian.PutUint32(page[18:], pageIndex) // Page sequence number
|
||||
page[26] = 1 // Number of segments in page, giving always 1 segment
|
||||
page[27] = uint8(i.lastPayloadSize) // Segment Table inserting at 27th position since page header length is 27
|
||||
copy(page[28:], payload) // inserting at 28th since Segment Table(1) + header length(27)
|
||||
|
||||
var checksum uint32
|
||||
for index := range page {
|
||||
checksum = (checksum << 8) ^ i.checksumTable[byte(checksum>>24)^page[index]]
|
||||
}
|
||||
binary.LittleEndian.PutUint32(page[22:], checksum) // Checksum - generating for page data and inserting at 22th position into 32 bits
|
||||
|
||||
return page
|
||||
}
|
||||
|
||||
// WriteRTP adds a new packet and writes the appropriate headers for it
|
||||
func (i *OggWriter) WriteRTP(packet *rtp.Packet) error {
|
||||
if packet == nil {
|
||||
return errInvalidNilPacket
|
||||
}
|
||||
|
||||
opusPacket := codecs.OpusPacket{}
|
||||
if _, err := opusPacket.Unmarshal(packet.Payload); err != nil {
|
||||
// Only handle Opus packets
|
||||
return err
|
||||
}
|
||||
|
||||
payload := opusPacket.Payload[0:]
|
||||
|
||||
// Should be equivalent to sampleRate * duration
|
||||
if i.previousTimestamp != 1 {
|
||||
increment := packet.Timestamp - i.previousTimestamp
|
||||
i.previousGranulePosition += uint64(increment)
|
||||
}
|
||||
i.previousTimestamp = packet.Timestamp
|
||||
|
||||
data := i.createPage(payload, pageHeaderTypeContinuationOfStream, i.previousGranulePosition, i.pageIndex)
|
||||
i.pageIndex++
|
||||
return i.writeToStream(data)
|
||||
}
|
||||
|
||||
// Close stops the recording
|
||||
func (i *OggWriter) Close() error {
|
||||
defer func() {
|
||||
i.fd = nil
|
||||
i.stream = nil
|
||||
}()
|
||||
|
||||
// Returns no error has it may be convenient to call
|
||||
// Close() multiple times
|
||||
if i.fd == nil {
|
||||
// Close stream if we are operating on a stream
|
||||
if closer, ok := i.stream.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Seek back one page, we need to update the header and generate new CRC
|
||||
pageOffset, err := i.fd.Seek(-1*int64(i.lastPayloadSize+pageHeaderSize+1), 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
payload := make([]byte, i.lastPayloadSize)
|
||||
if _, err := i.fd.ReadAt(payload, pageOffset+pageHeaderSize+1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := i.createPage(payload, pageHeaderTypeEndOfStream, i.previousGranulePosition, i.pageIndex-1)
|
||||
if err := i.writeToStream(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the last page if we are operating on files
|
||||
// to mark it as the EOS
|
||||
return i.fd.Close()
|
||||
}
|
||||
|
||||
// Wraps writing to the stream and maintains state
|
||||
// so we can set values for EOS
|
||||
func (i *OggWriter) writeToStream(p []byte) error {
|
||||
if i.stream == nil {
|
||||
return errFileNotOpened
|
||||
}
|
||||
|
||||
_, err := i.stream.Write(p)
|
||||
return err
|
||||
}
|
||||
|
||||
func generateChecksumTable() *[256]uint32 {
|
||||
var table [256]uint32
|
||||
const poly = 0x04c11db7
|
||||
|
||||
for i := range table {
|
||||
r := uint32(i) << 24
|
||||
for j := 0; j < 8; j++ {
|
||||
if (r & 0x80000000) != 0 {
|
||||
r = (r << 1) ^ poly
|
||||
} else {
|
||||
r <<= 1
|
||||
}
|
||||
table[i] = (r & 0xffffffff)
|
||||
}
|
||||
}
|
||||
return &table
|
||||
}
|
160
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go
generated
vendored
Normal file
160
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
// Package rtcerr implements the error wrappers defined throughout the
|
||||
// WebRTC 1.0 specifications.
|
||||
package rtcerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// UnknownError indicates the operation failed for an unknown transient reason.
|
||||
type UnknownError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *UnknownError) Error() string {
|
||||
return fmt.Sprintf("UnknownError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *UnknownError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// InvalidStateError indicates the object is in an invalid state.
|
||||
type InvalidStateError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *InvalidStateError) Error() string {
|
||||
return fmt.Sprintf("InvalidStateError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *InvalidStateError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// InvalidAccessError indicates the object does not support the operation or
|
||||
// argument.
|
||||
type InvalidAccessError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *InvalidAccessError) Error() string {
|
||||
return fmt.Sprintf("InvalidAccessError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *InvalidAccessError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// NotSupportedError indicates the operation is not supported.
|
||||
type NotSupportedError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *NotSupportedError) Error() string {
|
||||
return fmt.Sprintf("NotSupportedError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *NotSupportedError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// InvalidModificationError indicates the object cannot be modified in this way.
|
||||
type InvalidModificationError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *InvalidModificationError) Error() string {
|
||||
return fmt.Sprintf("InvalidModificationError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *InvalidModificationError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// SyntaxError indicates the string did not match the expected pattern.
|
||||
type SyntaxError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string {
|
||||
return fmt.Sprintf("SyntaxError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *SyntaxError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// TypeError indicates an error when a value is not of the expected type.
|
||||
type TypeError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *TypeError) Error() string {
|
||||
return fmt.Sprintf("TypeError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *TypeError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// OperationError indicates the operation failed for an operation-specific
|
||||
// reason.
|
||||
type OperationError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *OperationError) Error() string {
|
||||
return fmt.Sprintf("OperationError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *OperationError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// NotReadableError indicates the input/output read operation failed.
|
||||
type NotReadableError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *NotReadableError) Error() string {
|
||||
return fmt.Sprintf("NotReadableError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *NotReadableError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// RangeError indicates an error when a value is not in the set or range
|
||||
// of allowed values.
|
||||
type RangeError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *RangeError) Error() string {
|
||||
return fmt.Sprintf("RangeError: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
|
||||
// an Unwrap method returning error. Otherwise, Unwrap returns nil.
|
||||
func (e *RangeError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/renovate.json
generated
vendored
Normal file
15
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/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"]
|
||||
}
|
||||
]
|
||||
}
|
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go
generated
vendored
Normal file
31
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package webrtc
|
||||
|
||||
const (
|
||||
// TypeRTCPFBTransportCC ..
|
||||
TypeRTCPFBTransportCC = "transport-cc"
|
||||
|
||||
// TypeRTCPFBGoogREMB ..
|
||||
TypeRTCPFBGoogREMB = "goog-remb"
|
||||
|
||||
// TypeRTCPFBACK ..
|
||||
TypeRTCPFBACK = "ack"
|
||||
|
||||
// TypeRTCPFBCCM ..
|
||||
TypeRTCPFBCCM = "ccm"
|
||||
|
||||
// TypeRTCPFBNACK ..
|
||||
TypeRTCPFBNACK = "nack"
|
||||
)
|
||||
|
||||
// RTCPFeedback signals the connection to use additional RTCP packet types.
|
||||
// https://draft.ortc.org/#dom-rtcrtcpfeedback
|
||||
type RTCPFeedback struct {
|
||||
// Type is the type of feedback.
|
||||
// see: https://draft.ortc.org/#dom-rtcrtcpfeedback
|
||||
// valid: ack, ccm, nack, goog-remb, transport-cc
|
||||
Type string
|
||||
|
||||
// The parameter value depends on the type.
|
||||
// For example, type="nack" parameter="pli" will send Picture Loss Indicator packets.
|
||||
Parameter string
|
||||
}
|
66
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go
generated
vendored
Normal file
66
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// RTCPMuxPolicy affects what ICE candidates are gathered to support
|
||||
// non-multiplexed RTCP.
|
||||
type RTCPMuxPolicy int
|
||||
|
||||
const (
|
||||
// RTCPMuxPolicyNegotiate indicates to gather ICE candidates for both
|
||||
// RTP and RTCP candidates. If the remote-endpoint is capable of
|
||||
// multiplexing RTCP, multiplex RTCP on the RTP candidates. If it is not,
|
||||
// use both the RTP and RTCP candidates separately.
|
||||
RTCPMuxPolicyNegotiate RTCPMuxPolicy = iota + 1
|
||||
|
||||
// RTCPMuxPolicyRequire indicates to gather ICE candidates only for
|
||||
// RTP and multiplex RTCP on the RTP candidates. If the remote endpoint is
|
||||
// not capable of rtcp-mux, session negotiation will fail.
|
||||
RTCPMuxPolicyRequire
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
rtcpMuxPolicyNegotiateStr = "negotiate"
|
||||
rtcpMuxPolicyRequireStr = "require"
|
||||
)
|
||||
|
||||
func newRTCPMuxPolicy(raw string) RTCPMuxPolicy {
|
||||
switch raw {
|
||||
case rtcpMuxPolicyNegotiateStr:
|
||||
return RTCPMuxPolicyNegotiate
|
||||
case rtcpMuxPolicyRequireStr:
|
||||
return RTCPMuxPolicyRequire
|
||||
default:
|
||||
return RTCPMuxPolicy(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t RTCPMuxPolicy) String() string {
|
||||
switch t {
|
||||
case RTCPMuxPolicyNegotiate:
|
||||
return rtcpMuxPolicyNegotiateStr
|
||||
case RTCPMuxPolicyRequire:
|
||||
return rtcpMuxPolicyRequireStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (t *RTCPMuxPolicy) UnmarshalJSON(b []byte) error {
|
||||
var val string
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = newRTCPMuxPolicy(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (t RTCPMuxPolicy) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go
generated
vendored
Normal file
9
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
package webrtc
|
||||
|
||||
// RTPCapabilities represents the capabilities of a transceiver
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#rtcrtpcapabilities
|
||||
type RTPCapabilities struct {
|
||||
Codecs []RTPCodecCapability
|
||||
HeaderExtensions []RTPHeaderExtensionCapability
|
||||
}
|
107
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcodec.go
generated
vendored
Normal file
107
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcodec.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RTPCodecType determines the type of a codec
|
||||
type RTPCodecType int
|
||||
|
||||
const (
|
||||
|
||||
// RTPCodecTypeAudio indicates this is an audio codec
|
||||
RTPCodecTypeAudio RTPCodecType = iota + 1
|
||||
|
||||
// RTPCodecTypeVideo indicates this is a video codec
|
||||
RTPCodecTypeVideo
|
||||
)
|
||||
|
||||
func (t RTPCodecType) String() string {
|
||||
switch t {
|
||||
case RTPCodecTypeAudio:
|
||||
return "audio"
|
||||
case RTPCodecTypeVideo:
|
||||
return "video" //nolint: goconst
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// NewRTPCodecType creates a RTPCodecType from a string
|
||||
func NewRTPCodecType(r string) RTPCodecType {
|
||||
switch {
|
||||
case strings.EqualFold(r, RTPCodecTypeAudio.String()):
|
||||
return RTPCodecTypeAudio
|
||||
case strings.EqualFold(r, RTPCodecTypeVideo.String()):
|
||||
return RTPCodecTypeVideo
|
||||
default:
|
||||
return RTPCodecType(0)
|
||||
}
|
||||
}
|
||||
|
||||
// RTPCodecCapability provides information about codec capabilities.
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpcodeccapability-members
|
||||
type RTPCodecCapability struct {
|
||||
MimeType string
|
||||
ClockRate uint32
|
||||
Channels uint16
|
||||
SDPFmtpLine string
|
||||
RTCPFeedback []RTCPFeedback
|
||||
}
|
||||
|
||||
// RTPHeaderExtensionCapability is used to define a RFC5285 RTP header extension supported by the codec.
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#dom-rtcrtpcapabilities-headerextensions
|
||||
type RTPHeaderExtensionCapability struct {
|
||||
URI string
|
||||
}
|
||||
|
||||
// RTPHeaderExtensionParameter represents a negotiated RFC5285 RTP header extension.
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpheaderextensionparameters-members
|
||||
type RTPHeaderExtensionParameter struct {
|
||||
URI string
|
||||
ID int
|
||||
}
|
||||
|
||||
// RTPCodecParameters is a sequence containing the media codecs that an RtpSender
|
||||
// will choose from, as well as entries for RTX, RED and FEC mechanisms. This also
|
||||
// includes the PayloadType that has been negotiated
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#rtcrtpcodecparameters
|
||||
type RTPCodecParameters struct {
|
||||
RTPCodecCapability
|
||||
PayloadType PayloadType
|
||||
|
||||
statsID string
|
||||
}
|
||||
|
||||
// RTPParameters is a list of negotiated codecs and header extensions
|
||||
//
|
||||
// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpparameters-members
|
||||
type RTPParameters struct {
|
||||
HeaderExtensions []RTPHeaderExtensionParameter
|
||||
Codecs []RTPCodecParameters
|
||||
}
|
||||
|
||||
// Do a fuzzy find for a codec in the list of codecs
|
||||
// Used for lookup up a codec in an existing list to find a match
|
||||
func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecParameters) (RTPCodecParameters, error) {
|
||||
// First attempt to match on MimeType + SDPFmtpLine
|
||||
for _, c := range haystack {
|
||||
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
|
||||
c.RTPCodecCapability.SDPFmtpLine == needle.RTPCodecCapability.SDPFmtpLine {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to just MimeType
|
||||
for _, c := range haystack {
|
||||
if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
return RTPCodecParameters{}, ErrCodecNotFound
|
||||
}
|
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go
generated
vendored
Normal file
10
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package webrtc
|
||||
|
||||
// RTPCodingParameters provides information relating to both encoding and decoding.
|
||||
// This is a subset of the RFC since Pion WebRTC doesn't implement encoding/decoding itself
|
||||
// http://draft.ortc.org/#dom-rtcrtpcodingparameters
|
||||
type RTPCodingParameters struct {
|
||||
RID string `json:"rid"`
|
||||
SSRC SSRC `json:"ssrc"`
|
||||
PayloadType PayloadType `json:"payloadType"`
|
||||
}
|
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go
generated
vendored
Normal file
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package webrtc
|
||||
|
||||
// RTPDecodingParameters provides information relating to both encoding and decoding.
|
||||
// This is a subset of the RFC since Pion WebRTC doesn't implement decoding itself
|
||||
// http://draft.ortc.org/#dom-rtcrtpdecodingparameters
|
||||
type RTPDecodingParameters struct {
|
||||
RTPCodingParameters
|
||||
}
|
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go
generated
vendored
Normal file
8
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package webrtc
|
||||
|
||||
// RTPEncodingParameters provides information relating to both encoding and decoding.
|
||||
// This is a subset of the RFC since Pion WebRTC doesn't implement encoding itself
|
||||
// http://draft.ortc.org/#dom-rtcrtpencodingparameters
|
||||
type RTPEncodingParameters struct {
|
||||
RTPCodingParameters
|
||||
}
|
6
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go
generated
vendored
Normal file
6
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
package webrtc
|
||||
|
||||
// RTPReceiveParameters contains the RTP stack settings used by receivers
|
||||
type RTPReceiveParameters struct {
|
||||
Encodings []RTPDecodingParameters
|
||||
}
|
361
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpreceiver.go
generated
vendored
Normal file
361
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpreceiver.go
generated
vendored
Normal file
|
@ -0,0 +1,361 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/srtp/v2"
|
||||
"github.com/pion/webrtc/v3/internal/util"
|
||||
)
|
||||
|
||||
// trackStreams maintains a mapping of RTP/RTCP streams to a specific track
|
||||
// a RTPReceiver may contain multiple streams if we are dealing with Multicast
|
||||
type trackStreams struct {
|
||||
track *TrackRemote
|
||||
|
||||
rtpReadStream *srtp.ReadStreamSRTP
|
||||
rtpInterceptor interceptor.RTPReader
|
||||
|
||||
rtcpReadStream *srtp.ReadStreamSRTCP
|
||||
rtcpInterceptor interceptor.RTCPReader
|
||||
}
|
||||
|
||||
// RTPReceiver allows an application to inspect the receipt of a TrackRemote
|
||||
type RTPReceiver struct {
|
||||
kind RTPCodecType
|
||||
transport *DTLSTransport
|
||||
|
||||
tracks []trackStreams
|
||||
|
||||
closed, received chan interface{}
|
||||
mu sync.RWMutex
|
||||
|
||||
// A reference to the associated api object
|
||||
api *API
|
||||
}
|
||||
|
||||
// NewRTPReceiver constructs a new RTPReceiver
|
||||
func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTransport) (*RTPReceiver, error) {
|
||||
if transport == nil {
|
||||
return nil, errRTPReceiverDTLSTransportNil
|
||||
}
|
||||
|
||||
r := &RTPReceiver{
|
||||
kind: kind,
|
||||
transport: transport,
|
||||
api: api,
|
||||
closed: make(chan interface{}),
|
||||
received: make(chan interface{}),
|
||||
tracks: []trackStreams{},
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Transport returns the currently-configured *DTLSTransport or nil
|
||||
// if one has not yet been configured
|
||||
func (r *RTPReceiver) Transport() *DTLSTransport {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.transport
|
||||
}
|
||||
|
||||
// GetParameters describes the current configuration for the encoding and
|
||||
// transmission of media on the receiver's track.
|
||||
func (r *RTPReceiver) GetParameters() RTPParameters {
|
||||
return r.api.mediaEngine.getRTPParametersByKind(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
|
||||
}
|
||||
|
||||
// Track returns the RtpTransceiver TrackRemote
|
||||
func (r *RTPReceiver) Track() *TrackRemote {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
if len(r.tracks) != 1 {
|
||||
return nil
|
||||
}
|
||||
return r.tracks[0].track
|
||||
}
|
||||
|
||||
// Tracks returns the RtpTransceiver tracks
|
||||
// A RTPReceiver to support Simulcast may now have multiple tracks
|
||||
func (r *RTPReceiver) Tracks() []*TrackRemote {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var tracks []*TrackRemote
|
||||
for i := range r.tracks {
|
||||
tracks = append(tracks, r.tracks[i].track)
|
||||
}
|
||||
return tracks
|
||||
}
|
||||
|
||||
// Receive initialize the track and starts all the transports
|
||||
func (r *RTPReceiver) Receive(parameters RTPReceiveParameters) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
select {
|
||||
case <-r.received:
|
||||
return errRTPReceiverReceiveAlreadyCalled
|
||||
default:
|
||||
}
|
||||
defer close(r.received)
|
||||
|
||||
if len(parameters.Encodings) == 1 && parameters.Encodings[0].SSRC != 0 {
|
||||
t := trackStreams{
|
||||
track: newTrackRemote(
|
||||
r.kind,
|
||||
parameters.Encodings[0].SSRC,
|
||||
"",
|
||||
r,
|
||||
),
|
||||
}
|
||||
|
||||
globalParams := r.GetParameters()
|
||||
codec := RTPCodecCapability{}
|
||||
if len(globalParams.Codecs) != 0 {
|
||||
codec = globalParams.Codecs[0].RTPCodecCapability
|
||||
}
|
||||
|
||||
streamInfo := createStreamInfo("", parameters.Encodings[0].SSRC, 0, codec, globalParams.HeaderExtensions)
|
||||
var err error
|
||||
if t.rtpReadStream, t.rtpInterceptor, t.rtcpReadStream, t.rtcpInterceptor, err = r.streamsForSSRC(parameters.Encodings[0].SSRC, streamInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.tracks = append(r.tracks, t)
|
||||
} else {
|
||||
for _, encoding := range parameters.Encodings {
|
||||
r.tracks = append(r.tracks, trackStreams{
|
||||
track: newTrackRemote(
|
||||
r.kind,
|
||||
0,
|
||||
encoding.RID,
|
||||
r,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads incoming RTCP for this RTPReceiver
|
||||
func (r *RTPReceiver) Read(b []byte) (n int, a interceptor.Attributes, err error) {
|
||||
select {
|
||||
case <-r.received:
|
||||
return r.tracks[0].rtcpInterceptor.Read(b, a)
|
||||
case <-r.closed:
|
||||
return 0, nil, io.ErrClosedPipe
|
||||
}
|
||||
}
|
||||
|
||||
// ReadSimulcast reads incoming RTCP for this RTPReceiver for given rid
|
||||
func (r *RTPReceiver) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) {
|
||||
select {
|
||||
case <-r.received:
|
||||
for _, t := range r.tracks {
|
||||
if t.track != nil && t.track.rid == rid {
|
||||
return t.rtcpInterceptor.Read(b, a)
|
||||
}
|
||||
}
|
||||
return 0, nil, fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid)
|
||||
case <-r.closed:
|
||||
return 0, nil, io.ErrClosedPipe
|
||||
}
|
||||
}
|
||||
|
||||
// ReadRTCP is a convenience method that wraps Read and unmarshal for you.
|
||||
// It also runs any configured interceptors.
|
||||
func (r *RTPReceiver) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
|
||||
b := make([]byte, receiveMTU)
|
||||
i, attributes, err := r.Read(b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkts, err := rtcp.Unmarshal(b[:i])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pkts, attributes, nil
|
||||
}
|
||||
|
||||
// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you
|
||||
func (r *RTPReceiver) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) {
|
||||
b := make([]byte, receiveMTU)
|
||||
i, attributes, err := r.ReadSimulcast(b, rid)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkts, err := rtcp.Unmarshal(b[:i])
|
||||
return pkts, attributes, err
|
||||
}
|
||||
|
||||
func (r *RTPReceiver) haveReceived() bool {
|
||||
select {
|
||||
case <-r.received:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Stop irreversibly stops the RTPReceiver
|
||||
func (r *RTPReceiver) Stop() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
var err error
|
||||
|
||||
select {
|
||||
case <-r.closed:
|
||||
return err
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-r.received:
|
||||
for i := range r.tracks {
|
||||
errs := []error{}
|
||||
|
||||
if r.tracks[i].rtcpReadStream != nil {
|
||||
errs = append(errs, r.tracks[i].rtcpReadStream.Close())
|
||||
}
|
||||
|
||||
if r.tracks[i].rtpReadStream != nil {
|
||||
errs = append(errs, r.tracks[i].rtpReadStream.Close())
|
||||
}
|
||||
|
||||
err = util.FlattenErrs(errs)
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
close(r.closed)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *RTPReceiver) streamsForTrack(t *TrackRemote) *trackStreams {
|
||||
for i := range r.tracks {
|
||||
if r.tracks[i].track == t {
|
||||
return &r.tracks[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readRTP should only be called by a track, this only exists so we can keep state in one place
|
||||
func (r *RTPReceiver) readRTP(b []byte, reader *TrackRemote) (n int, a interceptor.Attributes, err error) {
|
||||
<-r.received
|
||||
if t := r.streamsForTrack(reader); t != nil {
|
||||
return t.rtpInterceptor.Read(b, a)
|
||||
}
|
||||
|
||||
return 0, nil, fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, reader.SSRC())
|
||||
}
|
||||
|
||||
// receiveForRid is the sibling of Receive expect for RIDs instead of SSRCs
|
||||
// It populates all the internal state for the given RID
|
||||
func (r *RTPReceiver) receiveForRid(rid string, params RTPParameters, ssrc SSRC) (*TrackRemote, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
for i := range r.tracks {
|
||||
if r.tracks[i].track.RID() == rid {
|
||||
r.tracks[i].track.mu.Lock()
|
||||
r.tracks[i].track.kind = r.kind
|
||||
r.tracks[i].track.codec = params.Codecs[0]
|
||||
r.tracks[i].track.params = params
|
||||
r.tracks[i].track.ssrc = ssrc
|
||||
streamInfo := createStreamInfo("", ssrc, params.Codecs[0].PayloadType, params.Codecs[0].RTPCodecCapability, params.HeaderExtensions)
|
||||
r.tracks[i].track.mu.Unlock()
|
||||
|
||||
var err error
|
||||
if r.tracks[i].rtpReadStream, r.tracks[i].rtpInterceptor, r.tracks[i].rtcpReadStream, r.tracks[i].rtcpInterceptor, err = r.streamsForSSRC(ssrc, streamInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.tracks[i].track, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %d", errRTPReceiverForSSRCTrackStreamNotFound, ssrc)
|
||||
}
|
||||
|
||||
func (r *RTPReceiver) streamsForSSRC(ssrc SSRC, streamInfo interceptor.StreamInfo) (*srtp.ReadStreamSRTP, interceptor.RTPReader, *srtp.ReadStreamSRTCP, interceptor.RTCPReader, error) {
|
||||
srtpSession, err := r.transport.getSRTPSession()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
rtpReadStream, err := srtpSession.OpenReadStream(uint32(ssrc))
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
rtpInterceptor := r.api.interceptor.BindRemoteStream(&streamInfo, interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
|
||||
n, err = rtpReadStream.Read(in)
|
||||
return n, a, err
|
||||
}))
|
||||
|
||||
srtcpSession, err := r.transport.getSRTCPSession()
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(ssrc))
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
rtcpInterceptor := r.api.interceptor.BindRTCPReader(interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
|
||||
n, err = rtcpReadStream.Read(in)
|
||||
return n, a, err
|
||||
}))
|
||||
|
||||
return rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor, nil
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the max amount of time the RTCP stream will block before returning. 0 is forever.
|
||||
func (r *RTPReceiver) SetReadDeadline(t time.Time) error {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
if err := r.tracks[0].rtcpReadStream.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever.
|
||||
func (r *RTPReceiver) SetReadDeadlineSimulcast(deadline time.Time, rid string) error {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
for _, t := range r.tracks {
|
||||
if t.track != nil && t.track.rid == rid {
|
||||
return t.rtcpReadStream.SetReadDeadline(deadline)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid)
|
||||
}
|
||||
|
||||
// setRTPReadDeadline sets the max amount of time the RTP stream will block before returning. 0 is forever.
|
||||
// This should be fired by calling SetReadDeadline on the TrackRemote
|
||||
func (r *RTPReceiver) setRTPReadDeadline(deadline time.Time, reader *TrackRemote) error {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
if t := r.streamsForTrack(reader); t != nil {
|
||||
return t.rtpReadStream.SetReadDeadline(deadline)
|
||||
}
|
||||
return fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, reader.SSRC())
|
||||
}
|
254
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpsender.go
generated
vendored
Normal file
254
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpsender.go
generated
vendored
Normal file
|
@ -0,0 +1,254 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/randutil"
|
||||
"github.com/pion/rtcp"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer
|
||||
type RTPSender struct {
|
||||
track TrackLocal
|
||||
|
||||
srtpStream *srtpWriterFuture
|
||||
rtcpInterceptor interceptor.RTCPReader
|
||||
|
||||
context TrackLocalContext
|
||||
|
||||
transport *DTLSTransport
|
||||
|
||||
payloadType PayloadType
|
||||
ssrc SSRC
|
||||
|
||||
// nolint:godox
|
||||
// TODO(sgotti) remove this when in future we'll avoid replacing
|
||||
// a transceiver sender since we can just check the
|
||||
// transceiver negotiation status
|
||||
negotiated bool
|
||||
|
||||
// A reference to the associated api object
|
||||
api *API
|
||||
id string
|
||||
|
||||
mu sync.RWMutex
|
||||
sendCalled, stopCalled chan struct{}
|
||||
}
|
||||
|
||||
// NewRTPSender constructs a new RTPSender
|
||||
func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSender, error) {
|
||||
if track == nil {
|
||||
return nil, errRTPSenderTrackNil
|
||||
} else if transport == nil {
|
||||
return nil, errRTPSenderDTLSTransportNil
|
||||
}
|
||||
|
||||
id, err := randutil.GenerateCryptoRandomString(32, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &RTPSender{
|
||||
track: track,
|
||||
transport: transport,
|
||||
api: api,
|
||||
sendCalled: make(chan struct{}),
|
||||
stopCalled: make(chan struct{}),
|
||||
ssrc: SSRC(randutil.NewMathRandomGenerator().Uint32()),
|
||||
id: id,
|
||||
srtpStream: &srtpWriterFuture{},
|
||||
}
|
||||
|
||||
r.srtpStream.rtpSender = r
|
||||
|
||||
r.rtcpInterceptor = r.api.interceptor.BindRTCPReader(interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
|
||||
n, err = r.srtpStream.Read(in)
|
||||
return n, a, err
|
||||
}))
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *RTPSender) isNegotiated() bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.negotiated
|
||||
}
|
||||
|
||||
func (r *RTPSender) setNegotiated() {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.negotiated = true
|
||||
}
|
||||
|
||||
// Transport returns the currently-configured *DTLSTransport or nil
|
||||
// if one has not yet been configured
|
||||
func (r *RTPSender) Transport() *DTLSTransport {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.transport
|
||||
}
|
||||
|
||||
// GetParameters describes the current configuration for the encoding and
|
||||
// transmission of media on the sender's track.
|
||||
func (r *RTPSender) GetParameters() RTPSendParameters {
|
||||
return RTPSendParameters{
|
||||
RTPParameters: r.api.mediaEngine.getRTPParametersByKind(
|
||||
r.track.Kind(),
|
||||
[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
|
||||
),
|
||||
Encodings: []RTPEncodingParameters{
|
||||
{
|
||||
RTPCodingParameters: RTPCodingParameters{
|
||||
SSRC: r.ssrc,
|
||||
PayloadType: r.payloadType,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Track returns the RTCRtpTransceiver track, or nil
|
||||
func (r *RTPSender) Track() TrackLocal {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.track
|
||||
}
|
||||
|
||||
// ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal.
|
||||
// The new track must be of the same media kind (audio, video, etc) and switching the track should not
|
||||
// require negotiation.
|
||||
func (r *RTPSender) ReplaceTrack(track TrackLocal) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.hasSent() && r.track != nil {
|
||||
if err := r.track.Unbind(r.context); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.hasSent() || track == nil {
|
||||
r.track = track
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := track.Bind(r.context); err != nil {
|
||||
// Re-bind the original track
|
||||
if _, reBindErr := r.track.Bind(r.context); reBindErr != nil {
|
||||
return reBindErr
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
r.track = track
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send Attempts to set the parameters controlling the sending of media.
|
||||
func (r *RTPSender) Send(parameters RTPSendParameters) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if r.hasSent() {
|
||||
return errRTPSenderSendAlreadyCalled
|
||||
}
|
||||
|
||||
writeStream := &interceptorToTrackLocalWriter{}
|
||||
r.context = TrackLocalContext{
|
||||
id: r.id,
|
||||
params: r.api.mediaEngine.getRTPParametersByKind(r.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
|
||||
ssrc: parameters.Encodings[0].SSRC,
|
||||
writeStream: writeStream,
|
||||
}
|
||||
|
||||
codec, err := r.track.Bind(r.context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.context.params.Codecs = []RTPCodecParameters{codec}
|
||||
|
||||
streamInfo := createStreamInfo(r.id, parameters.Encodings[0].SSRC, codec.PayloadType, codec.RTPCodecCapability, parameters.HeaderExtensions)
|
||||
rtpInterceptor := r.api.interceptor.BindLocalStream(&streamInfo, interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
|
||||
return r.srtpStream.WriteRTP(header, payload)
|
||||
}))
|
||||
writeStream.interceptor.Store(rtpInterceptor)
|
||||
|
||||
close(r.sendCalled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop irreversibly stops the RTPSender
|
||||
func (r *RTPSender) Stop() error {
|
||||
r.mu.Lock()
|
||||
|
||||
if stopped := r.hasStopped(); stopped {
|
||||
r.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
close(r.stopCalled)
|
||||
r.mu.Unlock()
|
||||
|
||||
if !r.hasSent() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.ReplaceTrack(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.srtpStream.Close()
|
||||
}
|
||||
|
||||
// Read reads incoming RTCP for this RTPReceiver
|
||||
func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) {
|
||||
select {
|
||||
case <-r.sendCalled:
|
||||
return r.rtcpInterceptor.Read(b, a)
|
||||
case <-r.stopCalled:
|
||||
return 0, nil, io.ErrClosedPipe
|
||||
}
|
||||
}
|
||||
|
||||
// ReadRTCP is a convenience method that wraps Read and unmarshals for you.
|
||||
func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
|
||||
b := make([]byte, receiveMTU)
|
||||
i, attributes, err := r.Read(b)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkts, err := rtcp.Unmarshal(b[:i])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pkts, attributes, nil
|
||||
}
|
||||
|
||||
// hasSent tells if data has been ever sent for this instance
|
||||
func (r *RTPSender) hasSent() bool {
|
||||
select {
|
||||
case <-r.sendCalled:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// hasStopped tells if stop has been called
|
||||
func (r *RTPSender) hasStopped() bool {
|
||||
select {
|
||||
case <-r.stopCalled:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go
generated
vendored
Normal file
7
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package webrtc
|
||||
|
||||
// RTPSendParameters contains the RTP stack settings used by receivers
|
||||
type RTPSendParameters struct {
|
||||
RTPParameters
|
||||
Encodings []RTPEncodingParameters
|
||||
}
|
187
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiver.go
generated
vendored
Normal file
187
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiver.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
|
||||
type RTPTransceiver struct {
|
||||
mid atomic.Value // string
|
||||
sender atomic.Value // *RTPSender
|
||||
receiver atomic.Value // *RTPReceiver
|
||||
direction atomic.Value // RTPTransceiverDirection
|
||||
|
||||
stopped bool
|
||||
kind RTPCodecType
|
||||
}
|
||||
|
||||
// Sender returns the RTPTransceiver's RTPSender if it has one
|
||||
func (t *RTPTransceiver) Sender() *RTPSender {
|
||||
if v := t.sender.Load(); v != nil {
|
||||
return v.(*RTPSender)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSender sets the RTPSender and Track to current transceiver
|
||||
func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) error {
|
||||
t.setSender(s)
|
||||
return t.setSendingTrack(track)
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setSender(s *RTPSender) {
|
||||
t.sender.Store(s)
|
||||
}
|
||||
|
||||
// Receiver returns the RTPTransceiver's RTPReceiver if it has one
|
||||
func (t *RTPTransceiver) Receiver() *RTPReceiver {
|
||||
if v := t.receiver.Load(); v != nil {
|
||||
return v.(*RTPReceiver)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMid sets the RTPTransceiver's mid. If it was already set, will return an error.
|
||||
func (t *RTPTransceiver) setMid(mid string) error {
|
||||
if currentMid := t.Mid(); currentMid != "" {
|
||||
return fmt.Errorf("%w: %s to %s", errRTPTransceiverCannotChangeMid, currentMid, mid)
|
||||
}
|
||||
t.mid.Store(mid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer.
|
||||
func (t *RTPTransceiver) Mid() string {
|
||||
if v := t.mid.Load(); v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Kind returns RTPTransceiver's kind.
|
||||
func (t *RTPTransceiver) Kind() RTPCodecType {
|
||||
return t.kind
|
||||
}
|
||||
|
||||
// Direction returns the RTPTransceiver's current direction
|
||||
func (t *RTPTransceiver) Direction() RTPTransceiverDirection {
|
||||
return t.direction.Load().(RTPTransceiverDirection)
|
||||
}
|
||||
|
||||
// Stop irreversibly stops the RTPTransceiver
|
||||
func (t *RTPTransceiver) Stop() error {
|
||||
if t.Sender() != nil {
|
||||
if err := t.Sender().Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if t.Receiver() != nil {
|
||||
if err := t.Receiver().Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t.setDirection(RTPTransceiverDirectionInactive)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setReceiver(r *RTPReceiver) {
|
||||
t.receiver.Store(r)
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) {
|
||||
t.direction.Store(d)
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error {
|
||||
if err := t.Sender().ReplaceTrack(track); err != nil {
|
||||
return err
|
||||
}
|
||||
if track == nil {
|
||||
t.setSender(nil)
|
||||
}
|
||||
|
||||
switch {
|
||||
case track != nil && t.Direction() == RTPTransceiverDirectionRecvonly:
|
||||
t.setDirection(RTPTransceiverDirectionSendrecv)
|
||||
case track != nil && t.Direction() == RTPTransceiverDirectionInactive:
|
||||
t.setDirection(RTPTransceiverDirectionSendonly)
|
||||
case track == nil && t.Direction() == RTPTransceiverDirectionSendrecv:
|
||||
t.setDirection(RTPTransceiverDirectionRecvonly)
|
||||
case track == nil && t.Direction() == RTPTransceiverDirectionSendonly:
|
||||
t.setDirection(RTPTransceiverDirectionInactive)
|
||||
default:
|
||||
return errRTPTransceiverSetSendingInvalidState
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findByMid(mid string, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
|
||||
for i, t := range localTransceivers {
|
||||
if t.Mid() == mid {
|
||||
return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, localTransceivers
|
||||
}
|
||||
|
||||
// Given a direction+type pluck a transceiver from the passed list
|
||||
// if no entry satisfies the requested type+direction return a inactive Transceiver
|
||||
func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransceiverDirection, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
|
||||
// Get direction order from most preferred to least
|
||||
getPreferredDirections := func() []RTPTransceiverDirection {
|
||||
switch remoteDirection {
|
||||
case RTPTransceiverDirectionSendrecv:
|
||||
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
|
||||
case RTPTransceiverDirectionSendonly:
|
||||
return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
|
||||
case RTPTransceiverDirectionRecvonly:
|
||||
return []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv}
|
||||
default:
|
||||
return []RTPTransceiverDirection{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, possibleDirection := range getPreferredDirections() {
|
||||
for i := range localTransceivers {
|
||||
t := localTransceivers[i]
|
||||
if t.Mid() == "" && t.kind == remoteKind && possibleDirection == t.Direction() {
|
||||
return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, localTransceivers
|
||||
}
|
||||
|
||||
// handleUnknownRTPPacket consumes a single RTP Packet and returns information that is helpful
|
||||
// for demuxing and handling an unknown SSRC (usually for Simulcast)
|
||||
func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID uint8) (mid, rid string, payloadType PayloadType, err error) {
|
||||
rp := &rtp.Packet{}
|
||||
if err = rp.Unmarshal(buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !rp.Header.Extension {
|
||||
return
|
||||
}
|
||||
|
||||
payloadType = PayloadType(rp.PayloadType)
|
||||
if payload := rp.GetExtension(midExtensionID); payload != nil {
|
||||
mid = string(payload)
|
||||
}
|
||||
|
||||
if payload := rp.GetExtension(streamIDExtensionID); payload != nil {
|
||||
rid = string(payload)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
85
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go
generated
vendored
Normal file
85
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package webrtc
|
||||
|
||||
// RTPTransceiverDirection indicates the direction of the RTPTransceiver.
|
||||
type RTPTransceiverDirection int
|
||||
|
||||
const (
|
||||
// RTPTransceiverDirectionSendrecv indicates the RTPSender will offer
|
||||
// to send RTP and RTPReceiver the will offer to receive RTP.
|
||||
RTPTransceiverDirectionSendrecv RTPTransceiverDirection = iota + 1
|
||||
|
||||
// RTPTransceiverDirectionSendonly indicates the RTPSender will offer
|
||||
// to send RTP.
|
||||
RTPTransceiverDirectionSendonly
|
||||
|
||||
// RTPTransceiverDirectionRecvonly indicates the RTPReceiver the will
|
||||
// offer to receive RTP.
|
||||
RTPTransceiverDirectionRecvonly
|
||||
|
||||
// RTPTransceiverDirectionInactive indicates the RTPSender won't offer
|
||||
// to send RTP and RTPReceiver the won't offer to receive RTP.
|
||||
RTPTransceiverDirectionInactive
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
rtpTransceiverDirectionSendrecvStr = "sendrecv"
|
||||
rtpTransceiverDirectionSendonlyStr = "sendonly"
|
||||
rtpTransceiverDirectionRecvonlyStr = "recvonly"
|
||||
rtpTransceiverDirectionInactiveStr = "inactive"
|
||||
)
|
||||
|
||||
// NewRTPTransceiverDirection defines a procedure for creating a new
|
||||
// RTPTransceiverDirection from a raw string naming the transceiver direction.
|
||||
func NewRTPTransceiverDirection(raw string) RTPTransceiverDirection {
|
||||
switch raw {
|
||||
case rtpTransceiverDirectionSendrecvStr:
|
||||
return RTPTransceiverDirectionSendrecv
|
||||
case rtpTransceiverDirectionSendonlyStr:
|
||||
return RTPTransceiverDirectionSendonly
|
||||
case rtpTransceiverDirectionRecvonlyStr:
|
||||
return RTPTransceiverDirectionRecvonly
|
||||
case rtpTransceiverDirectionInactiveStr:
|
||||
return RTPTransceiverDirectionInactive
|
||||
default:
|
||||
return RTPTransceiverDirection(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t RTPTransceiverDirection) String() string {
|
||||
switch t {
|
||||
case RTPTransceiverDirectionSendrecv:
|
||||
return rtpTransceiverDirectionSendrecvStr
|
||||
case RTPTransceiverDirectionSendonly:
|
||||
return rtpTransceiverDirectionSendonlyStr
|
||||
case RTPTransceiverDirectionRecvonly:
|
||||
return rtpTransceiverDirectionRecvonlyStr
|
||||
case RTPTransceiverDirectionInactive:
|
||||
return rtpTransceiverDirectionInactiveStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// Revers indicate the opposite direction
|
||||
func (t RTPTransceiverDirection) Revers() RTPTransceiverDirection {
|
||||
switch t {
|
||||
case RTPTransceiverDirectionSendonly:
|
||||
return RTPTransceiverDirectionRecvonly
|
||||
case RTPTransceiverDirectionRecvonly:
|
||||
return RTPTransceiverDirectionSendonly
|
||||
default:
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
func haveRTPTransceiverDirectionIntersection(haystack []RTPTransceiverDirection, needle []RTPTransceiverDirection) bool {
|
||||
for _, n := range needle {
|
||||
for _, h := range haystack {
|
||||
if n == h {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
12
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go
generated
vendored
Normal file
12
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package webrtc
|
||||
|
||||
// RTPTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() to provide configuration options for the new transceiver.
|
||||
type RTPTransceiverInit struct {
|
||||
Direction RTPTransceiverDirection
|
||||
SendEncodings []RTPEncodingParameters
|
||||
// Streams []*Track
|
||||
}
|
||||
|
||||
// RtpTransceiverInit is a temporary mapping while we fix case sensitivity
|
||||
// Deprecated: Use RTPTransceiverInit instead
|
||||
type RtpTransceiverInit = RTPTransceiverInit //nolint: stylecheck,golint
|
6
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go
generated
vendored
Normal file
6
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
package webrtc
|
||||
|
||||
// SCTPCapabilities indicates the capabilities of the SCTPTransport.
|
||||
type SCTPCapabilities struct {
|
||||
MaxMessageSize uint32 `json:"maxMessageSize"`
|
||||
}
|
379
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctptransport.go
generated
vendored
Normal file
379
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctptransport.go
generated
vendored
Normal file
|
@ -0,0 +1,379 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/datachannel"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/sctp"
|
||||
"github.com/pion/webrtc/v3/pkg/rtcerr"
|
||||
)
|
||||
|
||||
const sctpMaxChannels = uint16(65535)
|
||||
|
||||
// SCTPTransport provides details about the SCTP transport.
|
||||
type SCTPTransport struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
dtlsTransport *DTLSTransport
|
||||
|
||||
// State represents the current state of the SCTP transport.
|
||||
state SCTPTransportState
|
||||
|
||||
// SCTPTransportState doesn't have an enum to distinguish between New/Connecting
|
||||
// so we need a dedicated field
|
||||
isStarted bool
|
||||
|
||||
// MaxMessageSize represents the maximum size of data that can be passed to
|
||||
// DataChannel's send() method.
|
||||
maxMessageSize float64
|
||||
|
||||
// MaxChannels represents the maximum amount of DataChannel's that can
|
||||
// be used simultaneously.
|
||||
maxChannels *uint16
|
||||
|
||||
// OnStateChange func()
|
||||
|
||||
onErrorHandler func(error)
|
||||
|
||||
association *sctp.Association
|
||||
onDataChannelHandler func(*DataChannel)
|
||||
onDataChannelOpenedHandler func(*DataChannel)
|
||||
|
||||
// DataChannels
|
||||
dataChannels []*DataChannel
|
||||
dataChannelsOpened uint32
|
||||
dataChannelsRequested uint32
|
||||
dataChannelsAccepted uint32
|
||||
|
||||
api *API
|
||||
log logging.LeveledLogger
|
||||
}
|
||||
|
||||
// NewSCTPTransport creates a new SCTPTransport.
|
||||
// This constructor is part of the ORTC API. It is not
|
||||
// meant to be used together with the basic WebRTC API.
|
||||
func (api *API) NewSCTPTransport(dtls *DTLSTransport) *SCTPTransport {
|
||||
res := &SCTPTransport{
|
||||
dtlsTransport: dtls,
|
||||
state: SCTPTransportStateConnecting,
|
||||
api: api,
|
||||
log: api.settingEngine.LoggerFactory.NewLogger("ortc"),
|
||||
}
|
||||
|
||||
res.updateMessageSize()
|
||||
res.updateMaxChannels()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Transport returns the DTLSTransport instance the SCTPTransport is sending over.
|
||||
func (r *SCTPTransport) Transport() *DTLSTransport {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
|
||||
return r.dtlsTransport
|
||||
}
|
||||
|
||||
// GetCapabilities returns the SCTPCapabilities of the SCTPTransport.
|
||||
func (r *SCTPTransport) GetCapabilities() SCTPCapabilities {
|
||||
return SCTPCapabilities{
|
||||
MaxMessageSize: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Start the SCTPTransport. Since both local and remote parties must mutually
|
||||
// create an SCTPTransport, SCTP SO (Simultaneous Open) is used to establish
|
||||
// a connection over SCTP.
|
||||
func (r *SCTPTransport) Start(remoteCaps SCTPCapabilities) error {
|
||||
if r.isStarted {
|
||||
return nil
|
||||
}
|
||||
r.isStarted = true
|
||||
|
||||
if err := r.ensureDTLS(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sctpAssociation, err := sctp.Client(sctp.Config{
|
||||
NetConn: r.Transport().conn,
|
||||
LoggerFactory: r.api.settingEngine.LoggerFactory,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
r.association = sctpAssociation
|
||||
r.state = SCTPTransportStateConnected
|
||||
|
||||
go r.acceptDataChannels(sctpAssociation)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the SCTPTransport
|
||||
func (r *SCTPTransport) Stop() error {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
if r.association == nil {
|
||||
return nil
|
||||
}
|
||||
err := r.association.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.association = nil
|
||||
r.state = SCTPTransportStateClosed
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) ensureDTLS() error {
|
||||
dtlsTransport := r.Transport()
|
||||
if dtlsTransport == nil || dtlsTransport.conn == nil {
|
||||
return errSCTPTransportDTLS
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) acceptDataChannels(a *sctp.Association) {
|
||||
for {
|
||||
dc, err := datachannel.Accept(a, &datachannel.Config{
|
||||
LoggerFactory: r.api.settingEngine.LoggerFactory,
|
||||
})
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
r.log.Errorf("Failed to accept data channel: %v", err)
|
||||
r.onError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
maxRetransmits *uint16
|
||||
maxPacketLifeTime *uint16
|
||||
)
|
||||
val := uint16(dc.Config.ReliabilityParameter)
|
||||
ordered := true
|
||||
|
||||
switch dc.Config.ChannelType {
|
||||
case datachannel.ChannelTypeReliable:
|
||||
ordered = true
|
||||
case datachannel.ChannelTypeReliableUnordered:
|
||||
ordered = false
|
||||
case datachannel.ChannelTypePartialReliableRexmit:
|
||||
ordered = true
|
||||
maxRetransmits = &val
|
||||
case datachannel.ChannelTypePartialReliableRexmitUnordered:
|
||||
ordered = false
|
||||
maxRetransmits = &val
|
||||
case datachannel.ChannelTypePartialReliableTimed:
|
||||
ordered = true
|
||||
maxPacketLifeTime = &val
|
||||
case datachannel.ChannelTypePartialReliableTimedUnordered:
|
||||
ordered = false
|
||||
maxPacketLifeTime = &val
|
||||
default:
|
||||
}
|
||||
|
||||
sid := dc.StreamIdentifier()
|
||||
rtcDC, err := r.api.newDataChannel(&DataChannelParameters{
|
||||
ID: &sid,
|
||||
Label: dc.Config.Label,
|
||||
Protocol: dc.Config.Protocol,
|
||||
Negotiated: dc.Config.Negotiated,
|
||||
Ordered: ordered,
|
||||
MaxPacketLifeTime: maxPacketLifeTime,
|
||||
MaxRetransmits: maxRetransmits,
|
||||
}, r.api.settingEngine.LoggerFactory.NewLogger("ortc"))
|
||||
if err != nil {
|
||||
r.log.Errorf("Failed to accept data channel: %v", err)
|
||||
r.onError(err)
|
||||
return
|
||||
}
|
||||
|
||||
<-r.onDataChannel(rtcDC)
|
||||
rtcDC.handleOpen(dc)
|
||||
|
||||
r.lock.Lock()
|
||||
r.dataChannelsOpened++
|
||||
handler := r.onDataChannelOpenedHandler
|
||||
r.lock.Unlock()
|
||||
|
||||
if handler != nil {
|
||||
handler(rtcDC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnError sets an event handler which is invoked when
|
||||
// the SCTP connection error occurs.
|
||||
func (r *SCTPTransport) OnError(f func(err error)) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.onErrorHandler = f
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) onError(err error) {
|
||||
r.lock.RLock()
|
||||
handler := r.onErrorHandler
|
||||
r.lock.RUnlock()
|
||||
|
||||
if handler != nil {
|
||||
go handler(err)
|
||||
}
|
||||
}
|
||||
|
||||
// OnDataChannel sets an event handler which is invoked when a data
|
||||
// channel message arrives from a remote peer.
|
||||
func (r *SCTPTransport) OnDataChannel(f func(*DataChannel)) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.onDataChannelHandler = f
|
||||
}
|
||||
|
||||
// OnDataChannelOpened sets an event handler which is invoked when a data
|
||||
// channel is opened
|
||||
func (r *SCTPTransport) OnDataChannelOpened(f func(*DataChannel)) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.onDataChannelOpenedHandler = f
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) onDataChannel(dc *DataChannel) (done chan struct{}) {
|
||||
r.lock.Lock()
|
||||
r.dataChannels = append(r.dataChannels, dc)
|
||||
r.dataChannelsAccepted++
|
||||
handler := r.onDataChannelHandler
|
||||
r.lock.Unlock()
|
||||
|
||||
done = make(chan struct{})
|
||||
if handler == nil || dc == nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
|
||||
// Run this synchronously to allow setup done in onDataChannelFn()
|
||||
// to complete before datachannel event handlers might be called.
|
||||
go func() {
|
||||
handler(dc)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) updateMessageSize() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
var remoteMaxMessageSize float64 = 65536 // pion/webrtc#758
|
||||
var canSendSize float64 = 65536 // pion/webrtc#758
|
||||
|
||||
r.maxMessageSize = r.calcMessageSize(remoteMaxMessageSize, canSendSize)
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) calcMessageSize(remoteMaxMessageSize, canSendSize float64) float64 {
|
||||
switch {
|
||||
case remoteMaxMessageSize == 0 &&
|
||||
canSendSize == 0:
|
||||
return math.Inf(1)
|
||||
|
||||
case remoteMaxMessageSize == 0:
|
||||
return canSendSize
|
||||
|
||||
case canSendSize == 0:
|
||||
return remoteMaxMessageSize
|
||||
|
||||
case canSendSize > remoteMaxMessageSize:
|
||||
return remoteMaxMessageSize
|
||||
|
||||
default:
|
||||
return canSendSize
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) updateMaxChannels() {
|
||||
val := sctpMaxChannels
|
||||
r.maxChannels = &val
|
||||
}
|
||||
|
||||
// MaxChannels is the maximum number of RTCDataChannels that can be open simultaneously.
|
||||
func (r *SCTPTransport) MaxChannels() uint16 {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
if r.maxChannels == nil {
|
||||
return sctpMaxChannels
|
||||
}
|
||||
|
||||
return *r.maxChannels
|
||||
}
|
||||
|
||||
// State returns the current state of the SCTPTransport
|
||||
func (r *SCTPTransport) State() SCTPTransportState {
|
||||
r.lock.RLock()
|
||||
defer r.lock.RUnlock()
|
||||
return r.state
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) collectStats(collector *statsReportCollector) {
|
||||
r.lock.Lock()
|
||||
association := r.association
|
||||
r.lock.Unlock()
|
||||
|
||||
collector.Collecting()
|
||||
|
||||
stats := TransportStats{
|
||||
Timestamp: statsTimestampFrom(time.Now()),
|
||||
Type: StatsTypeTransport,
|
||||
ID: "sctpTransport",
|
||||
}
|
||||
|
||||
if association != nil {
|
||||
stats.BytesSent = association.BytesSent()
|
||||
stats.BytesReceived = association.BytesReceived()
|
||||
}
|
||||
|
||||
collector.Collect(stats.ID, stats)
|
||||
}
|
||||
|
||||
func (r *SCTPTransport) generateAndSetDataChannelID(dtlsRole DTLSRole, idOut **uint16) error {
|
||||
isChannelWithID := func(id uint16) bool {
|
||||
for _, d := range r.dataChannels {
|
||||
if d.id != nil && *d.id == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var id uint16
|
||||
if dtlsRole != DTLSRoleClient {
|
||||
id++
|
||||
}
|
||||
|
||||
max := r.MaxChannels()
|
||||
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
for ; id < max-1; id += 2 {
|
||||
if isChannelWithID(id) {
|
||||
continue
|
||||
}
|
||||
*idOut = &id
|
||||
return nil
|
||||
}
|
||||
|
||||
return &rtcerr.OperationError{Err: ErrMaxDataChannelID}
|
||||
}
|
54
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctptransportstate.go
generated
vendored
Normal file
54
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sctptransportstate.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package webrtc
|
||||
|
||||
// SCTPTransportState indicates the state of the SCTP transport.
|
||||
type SCTPTransportState int
|
||||
|
||||
const (
|
||||
// SCTPTransportStateConnecting indicates the SCTPTransport is in the
|
||||
// process of negotiating an association. This is the initial state of the
|
||||
// SCTPTransportState when an SCTPTransport is created.
|
||||
SCTPTransportStateConnecting SCTPTransportState = iota + 1
|
||||
|
||||
// SCTPTransportStateConnected indicates the negotiation of an
|
||||
// association is completed.
|
||||
SCTPTransportStateConnected
|
||||
|
||||
// SCTPTransportStateClosed indicates a SHUTDOWN or ABORT chunk is
|
||||
// received or when the SCTP association has been closed intentionally,
|
||||
// such as by closing the peer connection or applying a remote description
|
||||
// that rejects data or changes the SCTP port.
|
||||
SCTPTransportStateClosed
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
sctpTransportStateConnectingStr = "connecting"
|
||||
sctpTransportStateConnectedStr = "connected"
|
||||
sctpTransportStateClosedStr = "closed"
|
||||
)
|
||||
|
||||
func newSCTPTransportState(raw string) SCTPTransportState {
|
||||
switch raw {
|
||||
case sctpTransportStateConnectingStr:
|
||||
return SCTPTransportStateConnecting
|
||||
case sctpTransportStateConnectedStr:
|
||||
return SCTPTransportStateConnected
|
||||
case sctpTransportStateClosedStr:
|
||||
return SCTPTransportStateClosed
|
||||
default:
|
||||
return SCTPTransportState(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (s SCTPTransportState) String() string {
|
||||
switch s {
|
||||
case SCTPTransportStateConnecting:
|
||||
return sctpTransportStateConnectingStr
|
||||
case SCTPTransportStateConnected:
|
||||
return sctpTransportStateConnectedStr
|
||||
case SCTPTransportStateClosed:
|
||||
return sctpTransportStateClosedStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
643
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdp.go
generated
vendored
Normal file
643
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdp.go
generated
vendored
Normal file
|
@ -0,0 +1,643 @@
|
|||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/ice/v2"
|
||||
"github.com/pion/logging"
|
||||
"github.com/pion/sdp/v3"
|
||||
)
|
||||
|
||||
// trackDetails represents any media source that can be represented in a SDP
|
||||
// This isn't keyed by SSRC because it also needs to support rid based sources
|
||||
type trackDetails struct {
|
||||
mid string
|
||||
kind RTPCodecType
|
||||
streamID string
|
||||
id string
|
||||
ssrc SSRC
|
||||
rids []string
|
||||
}
|
||||
|
||||
func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackDetails {
|
||||
for i := range trackDetails {
|
||||
if trackDetails[i].ssrc == ssrc {
|
||||
return &trackDetails[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterTrackWithSSRC(incomingTracks []trackDetails, ssrc SSRC) []trackDetails {
|
||||
filtered := []trackDetails{}
|
||||
for i := range incomingTracks {
|
||||
if incomingTracks[i].ssrc != ssrc {
|
||||
filtered = append(filtered, incomingTracks[i])
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// extract all trackDetails from an SDP.
|
||||
func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) []trackDetails { // nolint:gocognit
|
||||
incomingTracks := []trackDetails{}
|
||||
rtxRepairFlows := map[uint32]bool{}
|
||||
|
||||
for _, media := range s.MediaDescriptions {
|
||||
// Plan B can have multiple tracks in a signle media section
|
||||
streamID := ""
|
||||
trackID := ""
|
||||
|
||||
// If media section is recvonly or inactive skip
|
||||
if _, ok := media.Attribute(sdp.AttrKeyRecvOnly); ok {
|
||||
continue
|
||||
} else if _, ok := media.Attribute(sdp.AttrKeyInactive); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
midValue := getMidValue(media)
|
||||
if midValue == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
codecType := NewRTPCodecType(media.MediaName.Media)
|
||||
if codecType == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, attr := range media.Attributes {
|
||||
switch attr.Key {
|
||||
case sdp.AttrKeySSRCGroup:
|
||||
split := strings.Split(attr.Value, " ")
|
||||
if split[0] == sdp.SemanticTokenFlowIdentification {
|
||||
// Add rtx ssrcs to blacklist, to avoid adding them as tracks
|
||||
// Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section
|
||||
// as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first
|
||||
// (2231627014) as specified in RFC5576
|
||||
if len(split) == 3 {
|
||||
_, err := strconv.ParseUint(split[1], 10, 32)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse SSRC: %v", err)
|
||||
continue
|
||||
}
|
||||
rtxRepairFlow, err := strconv.ParseUint(split[2], 10, 32)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse SSRC: %v", err)
|
||||
continue
|
||||
}
|
||||
rtxRepairFlows[uint32(rtxRepairFlow)] = true
|
||||
incomingTracks = filterTrackWithSSRC(incomingTracks, SSRC(rtxRepairFlow)) // Remove if rtx was added as track before
|
||||
}
|
||||
}
|
||||
|
||||
// Handle `a=msid:<stream_id> <track_label>` for Unified plan. The first value is the same as MediaStream.id
|
||||
// in the browser and can be used to figure out which tracks belong to the same stream. The browser should
|
||||
// figure this out automatically when an ontrack event is emitted on RTCPeerConnection.
|
||||
case sdp.AttrKeyMsid:
|
||||
split := strings.Split(attr.Value, " ")
|
||||
if len(split) == 2 {
|
||||
streamID = split[0]
|
||||
trackID = split[1]
|
||||
}
|
||||
|
||||
case sdp.AttrKeySSRC:
|
||||
split := strings.Split(attr.Value, " ")
|
||||
ssrc, err := strconv.ParseUint(split[0], 10, 32)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to parse SSRC: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if rtxRepairFlow := rtxRepairFlows[uint32(ssrc)]; rtxRepairFlow {
|
||||
continue // This ssrc is a RTX repair flow, ignore
|
||||
}
|
||||
|
||||
if len(split) == 3 && strings.HasPrefix(split[1], "msid:") {
|
||||
streamID = split[1][len("msid:"):]
|
||||
trackID = split[2]
|
||||
}
|
||||
|
||||
isNewTrack := true
|
||||
trackDetails := &trackDetails{}
|
||||
for i := range incomingTracks {
|
||||
if incomingTracks[i].ssrc == SSRC(ssrc) {
|
||||
trackDetails = &incomingTracks[i]
|
||||
isNewTrack = false
|
||||
}
|
||||
}
|
||||
|
||||
trackDetails.mid = midValue
|
||||
trackDetails.kind = codecType
|
||||
trackDetails.streamID = streamID
|
||||
trackDetails.id = trackID
|
||||
trackDetails.ssrc = SSRC(ssrc)
|
||||
|
||||
if isNewTrack {
|
||||
incomingTracks = append(incomingTracks, *trackDetails)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rids := getRids(media); len(rids) != 0 && trackID != "" && streamID != "" {
|
||||
newTrack := trackDetails{
|
||||
mid: midValue,
|
||||
kind: codecType,
|
||||
streamID: streamID,
|
||||
id: trackID,
|
||||
rids: []string{},
|
||||
}
|
||||
for rid := range rids {
|
||||
newTrack.rids = append(newTrack.rids, rid)
|
||||
}
|
||||
|
||||
incomingTracks = append(incomingTracks, newTrack)
|
||||
}
|
||||
}
|
||||
return incomingTracks
|
||||
}
|
||||
|
||||
func getRids(media *sdp.MediaDescription) map[string]string {
|
||||
rids := map[string]string{}
|
||||
for _, attr := range media.Attributes {
|
||||
if attr.Key == "rid" {
|
||||
split := strings.Split(attr.Value, " ")
|
||||
rids[split[0]] = attr.Value
|
||||
}
|
||||
}
|
||||
return rids
|
||||
}
|
||||
|
||||
func addCandidatesToMediaDescriptions(candidates []ICECandidate, m *sdp.MediaDescription, iceGatheringState ICEGatheringState) error {
|
||||
appendCandidateIfNew := func(c ice.Candidate, attributes []sdp.Attribute) {
|
||||
marshaled := c.Marshal()
|
||||
for _, a := range attributes {
|
||||
if marshaled == a.Value {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m.WithValueAttribute("candidate", marshaled)
|
||||
}
|
||||
|
||||
for _, c := range candidates {
|
||||
candidate, err := c.toICE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
candidate.SetComponent(1)
|
||||
appendCandidateIfNew(candidate, m.Attributes)
|
||||
|
||||
candidate.SetComponent(2)
|
||||
appendCandidateIfNew(candidate, m.Attributes)
|
||||
}
|
||||
|
||||
if iceGatheringState != ICEGatheringStateComplete {
|
||||
return nil
|
||||
}
|
||||
for _, a := range m.Attributes {
|
||||
if a.Key == "end-of-candidates" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
m.WithPropertyAttribute("end-of-candidates")
|
||||
return nil
|
||||
}
|
||||
|
||||
func addDataMediaSection(d *sdp.SessionDescription, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState) error {
|
||||
media := (&sdp.MediaDescription{
|
||||
MediaName: sdp.MediaName{
|
||||
Media: mediaSectionApplication,
|
||||
Port: sdp.RangedPort{Value: 9},
|
||||
Protos: []string{"UDP", "DTLS", "SCTP"},
|
||||
Formats: []string{"webrtc-datachannel"},
|
||||
},
|
||||
ConnectionInformation: &sdp.ConnectionInformation{
|
||||
NetworkType: "IN",
|
||||
AddressType: "IP4",
|
||||
Address: &sdp.Address{
|
||||
Address: "0.0.0.0",
|
||||
},
|
||||
},
|
||||
}).
|
||||
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()).
|
||||
WithValueAttribute(sdp.AttrKeyMID, midValue).
|
||||
WithPropertyAttribute(RTPTransceiverDirectionSendrecv.String()).
|
||||
WithPropertyAttribute("sctp-port:5000").
|
||||
WithICECredentials(iceParams.UsernameFragment, iceParams.Password)
|
||||
|
||||
for _, f := range dtlsFingerprints {
|
||||
media = media.WithFingerprint(f.Algorithm, strings.ToUpper(f.Value))
|
||||
}
|
||||
|
||||
if shouldAddCandidates {
|
||||
if err := addCandidatesToMediaDescriptions(candidates, media, iceGatheringState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d.WithMedia(media)
|
||||
return nil
|
||||
}
|
||||
|
||||
func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGatherer, iceGatheringState ICEGatheringState) *SessionDescription {
|
||||
if sessionDescription == nil || i == nil {
|
||||
return sessionDescription
|
||||
}
|
||||
|
||||
candidates, err := i.GetLocalCandidates()
|
||||
if err != nil {
|
||||
return sessionDescription
|
||||
}
|
||||
|
||||
parsed := sessionDescription.parsed
|
||||
if len(parsed.MediaDescriptions) > 0 {
|
||||
m := parsed.MediaDescriptions[0]
|
||||
if err = addCandidatesToMediaDescriptions(candidates, m, iceGatheringState); err != nil {
|
||||
return sessionDescription
|
||||
}
|
||||
}
|
||||
|
||||
sdp, err := parsed.Marshal()
|
||||
if err != nil {
|
||||
return sessionDescription
|
||||
}
|
||||
|
||||
return &SessionDescription{
|
||||
SDP: string(sdp),
|
||||
Type: sessionDescription.Type,
|
||||
}
|
||||
}
|
||||
|
||||
func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, mediaEngine *MediaEngine, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState, mediaSection mediaSection) (bool, error) {
|
||||
transceivers := mediaSection.transceivers
|
||||
if len(transceivers) < 1 {
|
||||
return false, errSDPZeroTransceivers
|
||||
}
|
||||
// Use the first transceiver to generate the section attributes
|
||||
t := transceivers[0]
|
||||
media := sdp.NewJSEPMediaDescription(t.kind.String(), []string{}).
|
||||
WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()).
|
||||
WithValueAttribute(sdp.AttrKeyMID, midValue).
|
||||
WithICECredentials(iceParams.UsernameFragment, iceParams.Password).
|
||||
WithPropertyAttribute(sdp.AttrKeyRTCPMux).
|
||||
WithPropertyAttribute(sdp.AttrKeyRTCPRsize)
|
||||
|
||||
codecs := mediaEngine.getCodecsByKind(t.kind)
|
||||
for _, codec := range codecs {
|
||||
name := strings.TrimPrefix(codec.MimeType, "audio/")
|
||||
name = strings.TrimPrefix(name, "video/")
|
||||
media.WithCodec(uint8(codec.PayloadType), name, codec.ClockRate, codec.Channels, codec.SDPFmtpLine)
|
||||
|
||||
for _, feedback := range codec.RTPCodecCapability.RTCPFeedback {
|
||||
media.WithValueAttribute("rtcp-fb", fmt.Sprintf("%d %s %s", codec.PayloadType, feedback.Type, feedback.Parameter))
|
||||
}
|
||||
}
|
||||
if len(codecs) == 0 {
|
||||
// Explicitly reject track if we don't have the codec
|
||||
d.WithMedia(&sdp.MediaDescription{
|
||||
MediaName: sdp.MediaName{
|
||||
Media: t.kind.String(),
|
||||
Port: sdp.RangedPort{Value: 0},
|
||||
Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
|
||||
Formats: []string{"0"},
|
||||
},
|
||||
})
|
||||
return false, nil
|
||||
}
|
||||
|
||||
directions := []RTPTransceiverDirection{}
|
||||
if t.Sender() != nil {
|
||||
directions = append(directions, RTPTransceiverDirectionSendonly)
|
||||
}
|
||||
if t.Receiver() != nil {
|
||||
directions = append(directions, RTPTransceiverDirectionRecvonly)
|
||||
}
|
||||
|
||||
parameters := mediaEngine.getRTPParametersByKind(t.kind, directions)
|
||||
for _, rtpExtension := range parameters.HeaderExtensions {
|
||||
extURL, err := url.Parse(rtpExtension.URI)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
media.WithExtMap(sdp.ExtMap{Value: rtpExtension.ID, URI: extURL})
|
||||
}
|
||||
|
||||
if len(mediaSection.ridMap) > 0 {
|
||||
recvRids := make([]string, 0, len(mediaSection.ridMap))
|
||||
|
||||
for rid := range mediaSection.ridMap {
|
||||
media.WithValueAttribute("rid", rid+" recv")
|
||||
recvRids = append(recvRids, rid)
|
||||
}
|
||||
// Simulcast
|
||||
media.WithValueAttribute("simulcast", "recv "+strings.Join(recvRids, ";"))
|
||||
}
|
||||
|
||||
for _, mt := range transceivers {
|
||||
if mt.Sender() != nil && mt.Sender().Track() != nil {
|
||||
track := mt.Sender().Track()
|
||||
media = media.WithMediaSource(uint32(mt.Sender().ssrc), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID())
|
||||
if !isPlanB {
|
||||
media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
media = media.WithPropertyAttribute(t.Direction().String())
|
||||
|
||||
for _, fingerprint := range dtlsFingerprints {
|
||||
media = media.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value))
|
||||
}
|
||||
|
||||
if shouldAddCandidates {
|
||||
if err := addCandidatesToMediaDescriptions(candidates, media, iceGatheringState); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
d.WithMedia(media)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type mediaSection struct {
|
||||
id string
|
||||
transceivers []*RTPTransceiver
|
||||
data bool
|
||||
ridMap map[string]string
|
||||
}
|
||||
|
||||
// populateSDP serializes a PeerConnections state into an SDP
|
||||
func populateSDP(d *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTLSFingerprint, mediaDescriptionFingerprint bool, isICELite bool, mediaEngine *MediaEngine, connectionRole sdp.ConnectionRole, candidates []ICECandidate, iceParams ICEParameters, mediaSections []mediaSection, iceGatheringState ICEGatheringState) (*sdp.SessionDescription, error) {
|
||||
var err error
|
||||
mediaDtlsFingerprints := []DTLSFingerprint{}
|
||||
|
||||
if mediaDescriptionFingerprint {
|
||||
mediaDtlsFingerprints = dtlsFingerprints
|
||||
}
|
||||
|
||||
bundleValue := "BUNDLE"
|
||||
bundleCount := 0
|
||||
appendBundle := func(midValue string) {
|
||||
bundleValue += " " + midValue
|
||||
bundleCount++
|
||||
}
|
||||
|
||||
for i, m := range mediaSections {
|
||||
if m.data && len(m.transceivers) != 0 {
|
||||
return nil, errSDPMediaSectionMediaDataChanInvalid
|
||||
} else if !isPlanB && len(m.transceivers) > 1 {
|
||||
return nil, errSDPMediaSectionMultipleTrackInvalid
|
||||
}
|
||||
|
||||
shouldAddID := true
|
||||
shouldAddCanidates := i == 0
|
||||
if m.data {
|
||||
if err = addDataMediaSection(d, shouldAddCanidates, mediaDtlsFingerprints, m.id, iceParams, candidates, connectionRole, iceGatheringState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
shouldAddID, err = addTransceiverSDP(d, isPlanB, shouldAddCanidates, mediaDtlsFingerprints, mediaEngine, m.id, iceParams, candidates, connectionRole, iceGatheringState, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if shouldAddID {
|
||||
appendBundle(m.id)
|
||||
}
|
||||
}
|
||||
|
||||
if !mediaDescriptionFingerprint {
|
||||
for _, fingerprint := range dtlsFingerprints {
|
||||
d.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value))
|
||||
}
|
||||
}
|
||||
|
||||
if isICELite {
|
||||
// RFC 5245 S15.3
|
||||
d = d.WithValueAttribute(sdp.AttrKeyICELite, sdp.AttrKeyICELite)
|
||||
}
|
||||
|
||||
return d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue), nil
|
||||
}
|
||||
|
||||
func getMidValue(media *sdp.MediaDescription) string {
|
||||
for _, attr := range media.Attributes {
|
||||
if attr.Key == "mid" {
|
||||
return attr.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func descriptionIsPlanB(desc *SessionDescription) bool {
|
||||
if desc == nil || desc.parsed == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
detectionRegex := regexp.MustCompile(`(?i)^(audio|video|data)$`)
|
||||
for _, media := range desc.parsed.MediaDescriptions {
|
||||
if len(detectionRegex.FindStringSubmatch(getMidValue(media))) == 2 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getPeerDirection(media *sdp.MediaDescription) RTPTransceiverDirection {
|
||||
for _, a := range media.Attributes {
|
||||
if direction := NewRTPTransceiverDirection(a.Key); direction != RTPTransceiverDirection(Unknown) {
|
||||
return direction
|
||||
}
|
||||
}
|
||||
return RTPTransceiverDirection(Unknown)
|
||||
}
|
||||
|
||||
func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) {
|
||||
fingerprints := []string{}
|
||||
|
||||
if fingerprint, haveFingerprint := desc.Attribute("fingerprint"); haveFingerprint {
|
||||
fingerprints = append(fingerprints, fingerprint)
|
||||
}
|
||||
|
||||
for _, m := range desc.MediaDescriptions {
|
||||
if fingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint {
|
||||
fingerprints = append(fingerprints, fingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
if len(fingerprints) < 1 {
|
||||
return "", "", ErrSessionDescriptionNoFingerprint
|
||||
}
|
||||
|
||||
for _, m := range fingerprints {
|
||||
if m != fingerprints[0] {
|
||||
return "", "", ErrSessionDescriptionConflictingFingerprints
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(fingerprints[0], " ")
|
||||
if len(parts) != 2 {
|
||||
return "", "", ErrSessionDescriptionInvalidFingerprint
|
||||
}
|
||||
return parts[1], parts[0], nil
|
||||
}
|
||||
|
||||
func extractICEDetails(desc *sdp.SessionDescription) (string, string, []ICECandidate, error) {
|
||||
candidates := []ICECandidate{}
|
||||
remotePwds := []string{}
|
||||
remoteUfrags := []string{}
|
||||
|
||||
if ufrag, haveUfrag := desc.Attribute("ice-ufrag"); haveUfrag {
|
||||
remoteUfrags = append(remoteUfrags, ufrag)
|
||||
}
|
||||
if pwd, havePwd := desc.Attribute("ice-pwd"); havePwd {
|
||||
remotePwds = append(remotePwds, pwd)
|
||||
}
|
||||
|
||||
for _, m := range desc.MediaDescriptions {
|
||||
if ufrag, haveUfrag := m.Attribute("ice-ufrag"); haveUfrag {
|
||||
remoteUfrags = append(remoteUfrags, ufrag)
|
||||
}
|
||||
if pwd, havePwd := m.Attribute("ice-pwd"); havePwd {
|
||||
remotePwds = append(remotePwds, pwd)
|
||||
}
|
||||
|
||||
for _, a := range m.Attributes {
|
||||
if a.IsICECandidate() {
|
||||
c, err := ice.UnmarshalCandidate(a.Value)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
candidate, err := newICECandidateFromICE(c)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
candidates = append(candidates, candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(remoteUfrags) == 0 {
|
||||
return "", "", nil, ErrSessionDescriptionMissingIceUfrag
|
||||
} else if len(remotePwds) == 0 {
|
||||
return "", "", nil, ErrSessionDescriptionMissingIcePwd
|
||||
}
|
||||
|
||||
for _, m := range remoteUfrags {
|
||||
if m != remoteUfrags[0] {
|
||||
return "", "", nil, ErrSessionDescriptionConflictingIceUfrag
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range remotePwds {
|
||||
if m != remotePwds[0] {
|
||||
return "", "", nil, ErrSessionDescriptionConflictingIcePwd
|
||||
}
|
||||
}
|
||||
|
||||
return remoteUfrags[0], remotePwds[0], candidates, nil
|
||||
}
|
||||
|
||||
func haveApplicationMediaSection(desc *sdp.SessionDescription) bool {
|
||||
for _, m := range desc.MediaDescriptions {
|
||||
if m.MediaName.Media == mediaSectionApplication {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getByMid(searchMid string, desc *SessionDescription) *sdp.MediaDescription {
|
||||
for _, m := range desc.parsed.MediaDescriptions {
|
||||
if mid, ok := m.Attribute(sdp.AttrKeyMID); ok && mid == searchMid {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// haveDataChannel return MediaDescription with MediaName equal application
|
||||
func haveDataChannel(desc *SessionDescription) *sdp.MediaDescription {
|
||||
for _, d := range desc.parsed.MediaDescriptions {
|
||||
if d.MediaName.Media == mediaSectionApplication {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParameters, err error) {
|
||||
s := &sdp.SessionDescription{
|
||||
MediaDescriptions: []*sdp.MediaDescription{m},
|
||||
}
|
||||
|
||||
for _, payloadStr := range m.MediaName.Formats {
|
||||
payloadType, err := strconv.Atoi(payloadStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
codec, err := s.GetCodecForPayloadType(uint8(payloadType))
|
||||
if err != nil {
|
||||
if payloadType == 0 {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channels := uint16(0)
|
||||
val, err := strconv.Atoi(codec.EncodingParameters)
|
||||
if err == nil {
|
||||
channels = uint16(val)
|
||||
}
|
||||
|
||||
feedback := []RTCPFeedback{}
|
||||
for _, raw := range codec.RTCPFeedback {
|
||||
split := strings.Split(raw, " ")
|
||||
entry := RTCPFeedback{Type: split[0]}
|
||||
if len(split) == 2 {
|
||||
entry.Parameter = split[1]
|
||||
}
|
||||
|
||||
feedback = append(feedback, entry)
|
||||
}
|
||||
|
||||
out = append(out, RTPCodecParameters{
|
||||
RTPCodecCapability: RTPCodecCapability{m.MediaName.Media + "/" + codec.Name, codec.ClockRate, channels, codec.Fmtp, feedback},
|
||||
PayloadType: PayloadType(payloadType),
|
||||
})
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func rtpExtensionsFromMediaDescription(m *sdp.MediaDescription) (map[string]int, error) {
|
||||
out := map[string]int{}
|
||||
|
||||
for _, a := range m.Attributes {
|
||||
if a.Key == sdp.AttrKeyExtMap {
|
||||
e := sdp.ExtMap{}
|
||||
if err := e.Unmarshal(a.String()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out[e.URI.String()] = e.Value
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
74
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdpsemantics.go
generated
vendored
Normal file
74
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdpsemantics.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// SDPSemantics determines which style of SDP offers and answers
|
||||
// can be used
|
||||
type SDPSemantics int
|
||||
|
||||
const (
|
||||
// SDPSemanticsUnifiedPlan uses unified-plan offers and answers
|
||||
// (the default in Chrome since M72)
|
||||
// https://tools.ietf.org/html/draft-roach-mmusic-unified-plan-00
|
||||
SDPSemanticsUnifiedPlan SDPSemantics = iota
|
||||
|
||||
// SDPSemanticsPlanB uses plan-b offers and answers
|
||||
// NB: This format should be considered deprecated
|
||||
// https://tools.ietf.org/html/draft-uberti-rtcweb-plan-00
|
||||
SDPSemanticsPlanB
|
||||
|
||||
// SDPSemanticsUnifiedPlanWithFallback prefers unified-plan
|
||||
// offers and answers, but will respond to a plan-b offer
|
||||
// with a plan-b answer
|
||||
SDPSemanticsUnifiedPlanWithFallback
|
||||
)
|
||||
|
||||
const (
|
||||
sdpSemanticsUnifiedPlanWithFallback = "unified-plan-with-fallback"
|
||||
sdpSemanticsUnifiedPlan = "unified-plan"
|
||||
sdpSemanticsPlanB = "plan-b"
|
||||
)
|
||||
|
||||
func newSDPSemantics(raw string) SDPSemantics {
|
||||
switch raw {
|
||||
case sdpSemanticsUnifiedPlan:
|
||||
return SDPSemanticsUnifiedPlan
|
||||
case sdpSemanticsPlanB:
|
||||
return SDPSemanticsPlanB
|
||||
case sdpSemanticsUnifiedPlanWithFallback:
|
||||
return SDPSemanticsUnifiedPlanWithFallback
|
||||
default:
|
||||
return SDPSemantics(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (s SDPSemantics) String() string {
|
||||
switch s {
|
||||
case SDPSemanticsUnifiedPlanWithFallback:
|
||||
return sdpSemanticsUnifiedPlanWithFallback
|
||||
case SDPSemanticsUnifiedPlan:
|
||||
return sdpSemanticsUnifiedPlan
|
||||
case SDPSemanticsPlanB:
|
||||
return sdpSemanticsPlanB
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses the JSON-encoded data and stores the result
|
||||
func (s *SDPSemantics) UnmarshalJSON(b []byte) error {
|
||||
var val string
|
||||
if err := json.Unmarshal(b, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*s = newSDPSemantics(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding
|
||||
func (s SDPSemantics) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(s.String())
|
||||
}
|
100
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdptype.go
generated
vendored
Normal file
100
trunk/3rdparty/srs-bench/vendor/github.com/pion/webrtc/v3/sdptype.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
package webrtc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SDPType describes the type of an SessionDescription.
|
||||
type SDPType int
|
||||
|
||||
const (
|
||||
// SDPTypeOffer indicates that a description MUST be treated as an SDP
|
||||
// offer.
|
||||
SDPTypeOffer SDPType = iota + 1
|
||||
|
||||
// SDPTypePranswer indicates that a description MUST be treated as an
|
||||
// SDP answer, but not a final answer. A description used as an SDP
|
||||
// pranswer may be applied as a response to an SDP offer, or an update to
|
||||
// a previously sent SDP pranswer.
|
||||
SDPTypePranswer
|
||||
|
||||
// SDPTypeAnswer indicates that a description MUST be treated as an SDP
|
||||
// final answer, and the offer-answer exchange MUST be considered complete.
|
||||
// A description used as an SDP answer may be applied as a response to an
|
||||
// SDP offer or as an update to a previously sent SDP pranswer.
|
||||
SDPTypeAnswer
|
||||
|
||||
// SDPTypeRollback indicates that a description MUST be treated as
|
||||
// canceling the current SDP negotiation and moving the SDP offer and
|
||||
// answer back to what it was in the previous stable state. Note the
|
||||
// local or remote SDP descriptions in the previous stable state could be
|
||||
// null if there has not yet been a successful offer-answer negotiation.
|
||||
SDPTypeRollback
|
||||
)
|
||||
|
||||
// This is done this way because of a linter.
|
||||
const (
|
||||
sdpTypeOfferStr = "offer"
|
||||
sdpTypePranswerStr = "pranswer"
|
||||
sdpTypeAnswerStr = "answer"
|
||||
sdpTypeRollbackStr = "rollback"
|
||||
)
|
||||
|
||||
// NewSDPType creates an SDPType from a string
|
||||
func NewSDPType(raw string) SDPType {
|
||||
switch raw {
|
||||
case sdpTypeOfferStr:
|
||||
return SDPTypeOffer
|
||||
case sdpTypePranswerStr:
|
||||
return SDPTypePranswer
|
||||
case sdpTypeAnswerStr:
|
||||
return SDPTypeAnswer
|
||||
case sdpTypeRollbackStr:
|
||||
return SDPTypeRollback
|
||||
default:
|
||||
return SDPType(Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
func (t SDPType) String() string {
|
||||
switch t {
|
||||
case SDPTypeOffer:
|
||||
return sdpTypeOfferStr
|
||||
case SDPTypePranswer:
|
||||
return sdpTypePranswerStr
|
||||
case SDPTypeAnswer:
|
||||
return sdpTypeAnswerStr
|
||||
case SDPTypeRollback:
|
||||
return sdpTypeRollbackStr
|
||||
default:
|
||||
return ErrUnknownType.Error()
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON enables JSON marshaling of a SDPType
|
||||
func (t SDPType) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON enables JSON unmarshaling of a SDPType
|
||||
func (t *SDPType) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
switch strings.ToLower(s) {
|
||||
default:
|
||||
return ErrUnknownType
|
||||
case "offer":
|
||||
*t = SDPTypeOffer
|
||||
case "pranswer":
|
||||
*t = SDPTypePranswer
|
||||
case "answer":
|
||||
*t = SDPTypeAnswer
|
||||
case "rollback":
|
||||
*t = SDPTypeRollback
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue