Showing preview only (2,174K chars total). Download the full file or copy to clipboard to get everything.
Repository: pion/webrtc
Branch: master
Commit: 87b7c3ec2bd3
Files: 388
Total size: 2.0 MB
Directory structure:
gitextract_laa6g7n2/
├── .codacy.yaml
├── .eslintrc.json
├── .github/
│ ├── .ci.conf
│ ├── .gitignore
│ ├── fetch-scripts.sh
│ ├── install-hooks.sh
│ ├── pion-gopher-webrtc.png.license
│ └── workflows/
│ ├── api.yaml
│ ├── browser-e2e.yaml
│ ├── codeql-analysis.yml
│ ├── examples-tests.yaml
│ ├── fuzz.yaml
│ ├── lint.yaml
│ ├── release.yml
│ ├── renovate-go-sum-fix.yaml
│ ├── reuse.yml
│ ├── standardjs.yaml
│ ├── test.yaml
│ └── tidy-check.yaml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── .reuse/
│ └── dep5
├── DESIGN.md
├── LICENSE
├── LICENSES/
│ └── MIT.txt
├── README.md
├── api.go
├── api_js.go
├── api_test.go
├── bundlepolicy.go
├── bundlepolicy_test.go
├── certificate.go
├── certificate_js_test.go
├── certificate_test.go
├── codecov.yml
├── configuration.go
├── configuration_common.go
├── configuration_js.go
├── configuration_test.go
├── constants.go
├── datachannel.go
├── datachannel_go_test.go
├── datachannel_js.go
├── datachannel_js_detach.go
├── datachannel_test.go
├── datachannelinit.go
├── datachannelmessage.go
├── datachannelparameters.go
├── datachannelstate.go
├── datachannelstate_test.go
├── dtlsfingerprint.go
├── dtlsparameters.go
├── dtlsrole.go
├── dtlsrole_test.go
├── dtlstransport.go
├── dtlstransport_js.go
├── dtlstransport_test.go
├── dtlstransportstate.go
├── dtlstransportstate_test.go
├── e2e/
│ ├── Dockerfile
│ ├── e2e_test.go
│ └── test.html
├── errors.go
├── examples/
│ ├── README.md
│ ├── bandwidth-estimation-from-disk/
│ │ ├── README.md
│ │ └── main.go
│ ├── broadcast/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── custom-logger/
│ │ ├── README.md
│ │ └── main.go
│ ├── data-channels/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ ├── demo.js
│ │ │ └── main.go
│ │ └── main.go
│ ├── data-channels-detach/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.html
│ │ │ └── main.go
│ │ └── main.go
│ ├── data-channels-detach-create/
│ │ ├── README.md
│ │ └── main.go
│ ├── data-channels-flow-control/
│ │ ├── README.md
│ │ └── main.go
│ ├── data-channels-simple/
│ │ ├── README.md
│ │ ├── demo.html
│ │ └── main.go
│ ├── data-channels-whip-whep-like/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── example.html
│ ├── examples.go
│ ├── examples.json
│ ├── ice-proxy/
│ │ ├── README.md
│ │ ├── answer.go
│ │ ├── main.go
│ │ ├── offer.go
│ │ ├── proxy.go
│ │ └── turn.go
│ ├── ice-restart/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── ice-single-port/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── ice-tcp/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── index.html
│ ├── insertable-streams/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── ortc/
│ │ ├── README.md
│ │ └── main.go
│ ├── ortc-media/
│ │ ├── README.md
│ │ └── main.go
│ ├── pion-to-pion/
│ │ ├── README.md
│ │ ├── answer/
│ │ │ ├── Dockerfile
│ │ │ └── main.go
│ │ ├── docker-compose.yml
│ │ ├── offer/
│ │ │ ├── Dockerfile
│ │ │ └── main.go
│ │ └── test.sh
│ ├── play-from-disk/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── play-from-disk-fec/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── play-from-disk-playlist-control/
│ │ ├── README.md
│ │ ├── main.go
│ │ └── web/
│ │ ├── app.css
│ │ ├── app.js
│ │ └── index.html
│ ├── play-from-disk-renegotiation/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── quick-switch/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── reflect/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── repacketize/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── index.js
│ │ └── main.go
│ ├── rtcp-processing/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── rtp-forwarder/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ ├── main.go
│ │ ├── rtp-forwarder.sdp
│ │ └── rtp-forwarder.sdp.license
│ ├── rtp-to-webrtc/
│ │ ├── README.md
│ │ └── main.go
│ ├── save-to-disk/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── save-to-disk-av1/
│ │ ├── README.md
│ │ └── main.go
│ ├── simulcast/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── stats/
│ │ ├── README.md
│ │ └── main.go
│ ├── swap-tracks/
│ │ ├── README.md
│ │ ├── jsfiddle/
│ │ │ ├── demo.css
│ │ │ ├── demo.details
│ │ │ ├── demo.html
│ │ │ └── demo.js
│ │ └── main.go
│ ├── trickle-ice/
│ │ ├── README.md
│ │ ├── index.html
│ │ └── main.go
│ ├── vnet/
│ │ ├── README.md
│ │ └── show-network-usage/
│ │ └── main.go
│ ├── warp/
│ │ ├── index.html
│ │ └── main.go
│ └── whip-whep/
│ ├── README.md
│ ├── index.html
│ └── main.go
├── gathering_complete_promise.go
├── gathering_complete_promise_example_test.go
├── go.mod
├── go.sum
├── ice_go.go
├── icecandidate.go
├── icecandidate_test.go
├── icecandidateinit.go
├── icecandidateinit_test.go
├── icecandidatepair.go
├── icecandidatepair_test.go
├── icecandidatetype.go
├── icecandidatetype_test.go
├── icecomponent.go
├── icecomponent_test.go
├── iceconnectionstate.go
├── iceconnectionstate_test.go
├── icecredentialtype.go
├── icecredentialtype_test.go
├── icegatherer.go
├── icegatherer_test.go
├── icegathererstate.go
├── icegathererstate_test.go
├── icegatheringstate.go
├── icegatheringstate_test.go
├── icegatheroptions.go
├── icemux.go
├── iceparameters.go
├── iceprotocol.go
├── iceprotocol_test.go
├── icerole.go
├── icerole_test.go
├── iceserver.go
├── iceserver_js.go
├── iceserver_test.go
├── icetransport.go
├── icetransport_js.go
├── icetransport_test.go
├── icetransportpolicy.go
├── icetransportpolicy_test.go
├── icetransportstate.go
├── icetransportstate_test.go
├── interceptor.go
├── interceptor_option.go
├── interceptor_test.go
├── internal/
│ ├── fmtp/
│ │ ├── av1.go
│ │ ├── fmtp.go
│ │ ├── fmtp_test.go
│ │ ├── h264.go
│ │ └── vp9.go
│ ├── mux/
│ │ ├── endpoint.go
│ │ ├── mux.go
│ │ ├── mux_test.go
│ │ └── muxfunc.go
│ └── util/
│ ├── util.go
│ └── util_test.go
├── js_utils.go
├── mediaengine.go
├── mediaengine_test.go
├── mimetype.go
├── networktype.go
├── networktype_test.go
├── oauthcredential.go
├── offeransweroptions.go
├── operations.go
├── operations_test.go
├── ortc_datachannel_test.go
├── ortc_media_test.go
├── ortc_test.go
├── package.json
├── peerconnection.go
├── peerconnection_close_test.go
├── peerconnection_go_test.go
├── peerconnection_js.go
├── peerconnection_js_test.go
├── peerconnection_media_test.go
├── peerconnection_renegotiation_test.go
├── peerconnection_test.go
├── peerconnectionstate.go
├── peerconnectionstate_test.go
├── pkg/
│ ├── media/
│ │ ├── h264reader/
│ │ │ ├── h264reader.go
│ │ │ ├── h264reader_test.go
│ │ │ └── nalunittype.go
│ │ ├── h264writer/
│ │ │ ├── h264writer.go
│ │ │ └── h264writer_test.go
│ │ ├── h265reader/
│ │ │ ├── h265reader.go
│ │ │ ├── h265reader_test.go
│ │ │ └── nalunittype.go
│ │ ├── h265writer/
│ │ │ ├── h265writer.go
│ │ │ └── h265writer_test.go
│ │ ├── ivfreader/
│ │ │ ├── ivfreader.go
│ │ │ └── ivfreader_test.go
│ │ ├── ivfwriter/
│ │ │ ├── ivfwriter.go
│ │ │ └── ivfwriter_test.go
│ │ ├── media.go
│ │ ├── media_test.go
│ │ ├── oggreader/
│ │ │ ├── oggreader.go
│ │ │ └── oggreader_test.go
│ │ ├── oggwriter/
│ │ │ ├── oggwriter.go
│ │ │ └── oggwriter_test.go
│ │ ├── rtpdump/
│ │ │ ├── reader.go
│ │ │ ├── reader_test.go
│ │ │ ├── rtpdump.go
│ │ │ ├── rtpdump_test.go
│ │ │ ├── writer.go
│ │ │ └── writer_test.go
│ │ └── samplebuilder/
│ │ ├── sampleSequenceLocation.go
│ │ ├── sampleSequenceLocation_test.go
│ │ ├── samplebuilder.go
│ │ └── samplebuilder_test.go
│ ├── null/
│ │ ├── null.go
│ │ └── null_test.go
│ └── rtcerr/
│ └── errors.go
├── renovate.json
├── rtcpfeedback.go
├── rtcpmuxpolicy.go
├── rtcpmuxpolicy_test.go
├── rtpcapabilities.go
├── rtpcodec.go
├── rtpcodec_test.go
├── rtpcodingparameters.go
├── rtpdecodingparameters.go
├── rtpencodingparameters.go
├── rtpreceiveparameters.go
├── rtpreceiver.go
├── rtpreceiver_go.go
├── rtpreceiver_go_test.go
├── rtpreceiver_js.go
├── rtpreceiver_test.go
├── rtpsender.go
├── rtpsender_js.go
├── rtpsender_test.go
├── rtpsendparameters.go
├── rtptransceiver.go
├── rtptransceiver_js.go
├── rtptransceiver_test.go
├── rtptransceiverdirection.go
├── rtptransceiverdirection_test.go
├── rtptransceiverinit.go
├── rtptransceiverinit_go_test.go
├── sctpcapabilities.go
├── sctptransport.go
├── sctptransport_js.go
├── sctptransport_test.go
├── sctptransportstate.go
├── sctptransportstate_test.go
├── sdp.go
├── sdp_test.go
├── sdpsemantics.go
├── sdpsemantics_test.go
├── sdptype.go
├── sdptype_test.go
├── sessiondescription.go
├── sessiondescription_test.go
├── settingengine.go
├── settingengine_js.go
├── settingengine_test.go
├── signalingstate.go
├── signalingstate_test.go
├── srtp_writer_future.go
├── srtp_writer_future_test.go
├── stats.go
├── stats_go.go
├── stats_go_test.go
├── test-wasm/
│ ├── LICENSE
│ ├── go_js_wasm_exec
│ └── node_shim.js
├── track_local.go
├── track_local_static.go
├── track_local_static_test.go
├── track_remote.go
├── track_remote_test.go
├── vnet_test.go
└── webrtc.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .codacy.yaml
================================================
---
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
exclude_paths:
- examples/examples.json
================================================
FILE: .eslintrc.json
================================================
{
"extends": ["standard"]
}
================================================
FILE: .github/.ci.conf
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
GO_JS_WASM_EXEC=${PWD}/test-wasm/go_js_wasm_exec
EXCLUDED_CONTRIBUTORS=('Josh Bleecher Snyder' 'Sidney San Martín')
================================================
FILE: .github/.gitignore
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
.goassets
================================================
FILE: .github/fetch-scripts.sh
================================================
#!/bin/sh
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
set -eu
SCRIPT_PATH="$(realpath "$(dirname "$0")")"
GOASSETS_PATH="${SCRIPT_PATH}/.goassets"
GOASSETS_REF=${GOASSETS_REF:-master}
if [ -d "${GOASSETS_PATH}" ]; then
if ! git -C "${GOASSETS_PATH}" diff --exit-code; then
echo "${GOASSETS_PATH} has uncommitted changes" >&2
exit 1
fi
git -C "${GOASSETS_PATH}" fetch origin
git -C "${GOASSETS_PATH}" checkout ${GOASSETS_REF}
git -C "${GOASSETS_PATH}" reset --hard origin/${GOASSETS_REF}
else
git clone -b ${GOASSETS_REF} https://github.com/pion/.goassets.git "${GOASSETS_PATH}"
fi
================================================
FILE: .github/install-hooks.sh
================================================
#!/bin/sh
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
SCRIPT_PATH="$(realpath "$(dirname "$0")")"
. ${SCRIPT_PATH}/fetch-scripts.sh
cp "${GOASSETS_PATH}/hooks/commit-msg.sh" "${SCRIPT_PATH}/../.git/hooks/commit-msg"
cp "${GOASSETS_PATH}/hooks/pre-commit.sh" "${SCRIPT_PATH}/../.git/hooks/pre-commit"
================================================
FILE: .github/pion-gopher-webrtc.png.license
================================================
SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
SPDX-License-Identifier: MIT
================================================
FILE: .github/workflows/api.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: API
on:
pull_request:
jobs:
check:
uses: pion/.goassets/.github/workflows/api.reusable.yml@master
================================================
FILE: .github/workflows/browser-e2e.yaml
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Browser E2E
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
e2e-test:
name: Test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v6
- name: test
run: |
docker build -t pion-webrtc-e2e -f e2e/Dockerfile .
docker run -i --rm pion-webrtc-e2e
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: CodeQL
on:
workflow_dispatch:
schedule:
- cron: '23 5 * * 0'
pull_request:
branches:
- master
paths:
- '**.go'
jobs:
analyze:
uses: pion/.goassets/.github/workflows/codeql-analysis.reusable.yml@master
================================================
FILE: .github/workflows/examples-tests.yaml
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Examples Tests
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
pion-to-pion-test:
name: Test
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v6
- name: test
run: cd examples/pion-to-pion && ./test.sh
================================================
FILE: .github/workflows/fuzz.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Fuzz
on:
push:
branches:
- master
schedule:
- cron: "0 */8 * * *"
jobs:
fuzz:
uses: pion/.goassets/.github/workflows/fuzz.reusable.yml@master
with:
go-version: "1.25" # auto-update/latest-go-version
fuzz-time: "60s"
================================================
FILE: .github/workflows/lint.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Lint
on:
pull_request:
jobs:
lint:
uses: pion/.goassets/.github/workflows/lint.reusable.yml@master
================================================
FILE: .github/workflows/release.yml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
uses: pion/.goassets/.github/workflows/release.reusable.yml@master
with:
go-version: "1.25" # auto-update/latest-go-version
================================================
FILE: .github/workflows/renovate-go-sum-fix.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Fix go.sum
on:
push:
branches:
- renovate/*
jobs:
fix:
uses: pion/.goassets/.github/workflows/renovate-go-sum-fix.reusable.yml@master
secrets:
token: ${{ secrets.PIONBOT_PRIVATE_KEY }}
================================================
FILE: .github/workflows/reuse.yml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: REUSE Compliance Check
on:
push:
pull_request:
jobs:
lint:
uses: pion/.goassets/.github/workflows/reuse.reusable.yml@master
================================================
FILE: .github/workflows/standardjs.yaml
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: StandardJS
on:
pull_request:
types:
- opened
- edited
- synchronize
jobs:
StandardJS:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v5
with:
node-version: 24.x
- run: npm install standard
- run: npx standard
================================================
FILE: .github/workflows/test.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Test
on:
push:
branches:
- master
pull_request:
jobs:
test:
uses: pion/.goassets/.github/workflows/test.reusable.yml@master
strategy:
matrix:
go: ["1.25", "1.24"] # auto-update/supported-go-version-list
fail-fast: false
with:
go-version: ${{ matrix.go }}
secrets: inherit
test-i386:
uses: pion/.goassets/.github/workflows/test-i386.reusable.yml@master
strategy:
matrix:
go: ["1.25", "1.24"] # auto-update/supported-go-version-list
fail-fast: false
with:
go-version: ${{ matrix.go }}
test-windows:
uses: pion/.goassets/.github/workflows/test-windows.reusable.yml@master
strategy:
matrix:
go: ["1.25", "1.24"] # auto-update/supported-go-version-list
fail-fast: false
with:
go-version: ${{ matrix.go }}
test-macos:
uses: pion/.goassets/.github/workflows/test-macos.reusable.yml@master
strategy:
matrix:
go: ["1.25", "1.24"] # auto-update/supported-go-version-list
fail-fast: false
with:
go-version: ${{ matrix.go }}
test-wasm:
uses: pion/.goassets/.github/workflows/test-wasm.reusable.yml@master
with:
go-version: "1.25" # auto-update/latest-go-version
secrets: inherit
================================================
FILE: .github/workflows/tidy-check.yaml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
# If this repository should have package specific CI config,
# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
#
# If you want to update the shared CI config, send a PR to
# https://github.com/pion/.goassets instead of this repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
name: Go mod tidy
on:
pull_request:
push:
branches:
- master
jobs:
tidy:
uses: pion/.goassets/.github/workflows/tidy-check.reusable.yml@master
with:
go-version: "1.25" # auto-update/latest-go-version
================================================
FILE: .gitignore
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
### JetBrains IDE ###
#####################
.idea/
### Emacs Temporary Files ###
#############################
*~
### Folders ###
###############
bin/
vendor/
node_modules/
### Files ###
#############
*.ivf
*.ogg
tags
cover.out
*.sw[poe]
*.wasm
examples/sfu-ws/cert.pem
examples/sfu-ws/key.pem
wasm_exec.js
================================================
FILE: .golangci.yml
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
version: "2"
linters:
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
- bidichk # Checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- containedctx # containedctx is a linter that detects struct contained context.Context field
- contextcheck # check the function whether use a non-inherited context
- cyclop # checks function and package cyclomatic complexity
- decorder # check declaration order and count of types, constants, variables and functions
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
- dupl # Tool for code clone detection
- durationcheck # check for two durations multiplied together
- err113 # Golang linter to check the errors handling expressions
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
- exhaustive # check exhaustiveness of enum switch statements
- forbidigo # Forbids identifiers
- forcetypeassert # finds forced type assertions
- gochecknoglobals # Checks that no globals 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
- gocyclo # Computes and checks the cyclomatic complexity of functions
- godot # Check if comments end in a period
- godox # Tool for detection of FIXME, TODO and other comment keywords
- goheader # Checks is file header matches to pattern
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
- goprintffuncname # Checks that printf-like functions are named with `f` at the end
- gosec # Inspects source code for security problems
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- grouper # An analyzer to analyze expression groups.
- importas # Enforces consistent import aliases
- ineffassign # Detects when assignments to existing variables are not used
- lll # Reports long lines
- maintidx # maintidx measures the maintainability index of each function.
- makezero # Finds slice declarations with non-zero initial length
- misspell # Finds commonly misspelled English words in comments
- modernize # Replace and suggests simplifications to code
- nakedret # Finds naked returns in functions greater than a specified function length
- nestif # Reports deeply nested if statements
- nilerr # Finds the code that returns nil even if it checks that the error is not nil.
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
- noctx # noctx finds sending http request without context.Context
- predeclared # find code that shadows one of Go's predeclared identifiers
- revive # golint replacement, finds style mistakes
- staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
- tagliatelle # Checks the struct tags.
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
- unconvert # Remove unnecessary type conversions
- unparam # Reports unused function parameters
- unused # Checks Go code for unused constants, variables, functions and types
- varnamelen # checks that the length of a variable's name matches its scope
- wastedassign # wastedassign finds wasted assignment statements
- whitespace # Tool for detection of leading and trailing whitespace
disable:
- depguard # Go linter that checks if package imports are in a list of acceptable packages
- funlen # Tool for detection of long functions
- gochecknoinits # Checks that no init functions are present in Go code
- 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.
- interfacebloat # A linter that checks length of interface.
- ireturn # Accept Interfaces, Return Concrete Types
- mnd # An analyzer to detect magic numbers
- nolintlint # Reports ill-formed or insufficient nolint directives
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
- prealloc # Finds slice declarations that could potentially be preallocated
- promlinter # Check Prometheus metrics naming via promlint
- rowserrcheck # checks whether Err of rows is checked successfully
- sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
- testpackage # linter that makes you use a separate _test package
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
- wrapcheck # Checks that errors returned from external packages are wrapped
- wsl # Whitespace Linter - Forces you to use empty lines!
settings:
staticcheck:
checks:
- all
- -QF1008 # "could remove embedded field", to keep it explicit!
- -QF1003 # "could use tagged switch on enum", Cases conflicts with exhaustive!
exhaustive:
default-signifies-exhaustive: true
forbidigo:
forbid:
- pattern: ^fmt.Print(f|ln)?$
- pattern: ^log.(Panic|Fatal|Print)(f|ln)?$
- pattern: ^os.Exit$
- pattern: ^panic$
- pattern: ^print(ln)?$
- pattern: ^testing.T.(Error|Errorf|Fatal|Fatalf|Fail|FailNow)$
pkg: ^testing$
msg: use testify/assert instead
analyze-types: true
gomodguard:
blocked:
modules:
- github.com/pkg/errors:
recommendations:
- errors
govet:
enable:
- shadow
revive:
rules:
# Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility
- name: use-any
severity: warning
disabled: false
misspell:
locale: US
varnamelen:
max-distance: 12
min-name-length: 2
ignore-type-assert-ok: true
ignore-map-index-ok: true
ignore-chan-recv-ok: true
ignore-decls:
- i int
- n int
- w io.Writer
- r io.Reader
- b []byte
exclusions:
generated: lax
rules:
- linters:
- forbidigo
- gocognit
path: (examples|main\.go)
- linters:
- gocognit
path: _test\.go
- linters:
- forbidigo
path: cmd
formatters:
enable:
- gci # Gci control golang package import order and make it always deterministic.
- 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.
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
exclusions:
generated: lax
================================================
FILE: .goreleaser.yml
================================================
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
builds:
- skip: true
================================================
FILE: .reuse/dep5
================================================
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Pion
Source: https://github.com/pion/
Files: README.md DESIGN.md **/README.md AUTHORS.txt renovate.json go.mod go.sum **/go.mod **/go.sum .eslintrc.json package.json examples.json sfu-ws/flutter/.gitignore sfu-ws/flutter/pubspec.yaml c-data-channels/webrtc.h examples/examples.json yarn.lock
Copyright: 2026 The Pion community <https://pion.ly>
License: MIT
Files: testdata/seed/* testdata/fuzz/* **/testdata/fuzz/* api/*.txt
Copyright: 2026 The Pion community <https://pion.ly>
License: CC0-1.0
================================================
FILE: DESIGN.md
================================================
<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/v4)
### 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.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2026 The Pion community <https://pion.ly>
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.
================================================
FILE: LICENSES/MIT.txt
================================================
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<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://discord.gg/PngbdqpFbt"><img src="https://img.shields.io/badge/join-us%20on%20discord-gray.svg?longCache=true&logo=discord&colorB=brightblue" alt="join us on Discord"></a> <a href="https://bsky.app/profile/pion.ly"><img src="https://img.shields.io/badge/follow-us%20on%20bluesky-gray.svg?longCache=true&logo=bluesky&colorB=brightblue" alt="Follow us on Bluesky"></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>
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pion/webrtc/test.yaml">
<a href="https://pkg.go.dev/github.com/pion/webrtc/v4"><img src="https://pkg.go.dev/badge/github.com/pion/webrtc/v4.svg" alt="Go Reference"></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/v4"><img src="https://goreportcard.com/badge/github.com/pion/webrtc/v4" alt="Go Report Card"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
<br>
### New Release
Pion WebRTC v4.0.0 has been released! See the [release notes](https://github.com/pion/webrtc/wiki/Release-WebRTC@v4.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 `v3` release.
We would love your feedback! Please create GitHub issues or Join the [Discord](https://discord.gg/PngbdqpFbt) 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 `/v4` (or an earlier version) 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/v4)** 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 [Discord](https://discord.gg/PngbdqpFbt) 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.
### Need Help?
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.
Pion has an active community on [Discord](https://discord.gg/PngbdqpFbt). Please ask for help about anything, questions don't have to be Pion specific!
Come share your interesting project you are working on. We are here to support you.
One of the maintainers of Pion [Sean-Der](https://github.com/sean-der) is available to help. Schedule at [siobud.com/meeting](https://siobud.com/meeting)
He is available to talk about Pion or general WebRTC questions, feel free to reach out about anything!
### 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/v4#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)
* [Sender/Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report)
* [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc)
* [Bandwidth Estimation](https://github.com/pion/webrtc/tree/master/examples/bandwidth-estimation-from-disk)
#### 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.
### Sponsoring
Work on Pion's congestion control and bandwidth estimation was funded through the [User-Operated Internet](https://nlnet.nl/useroperated/) fund, a fund established by [NLnet](https://nlnet.nl/) made possible by financial support from the [PKT Community](https://pkt.cash/)/[The Network Steward](https://pkt.cash/network-steward) and stichting [Technology Commons Trust](https://technologycommons.org/).
### Community
Pion has an active community on the [Discord](https://discord.gg/PngbdqpFbt).
Follow the [Pion Bluesky](https://bsky.app/profile/pion.ly) or [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
We are always looking to support **your projects**. Please reach out if you have something to build!
If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
### Contributing
Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
### License
MIT License - see [LICENSE](LICENSE) for full text
================================================
FILE: api.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"github.com/pion/interceptor"
"github.com/pion/logging"
)
// API allows configuration of a PeerConnection
// with APIs that are available in the standard. This
// lets you set custom behavior via the SettingEngine, configure
// codecs via the MediaEngine and define custom media behaviors via
// Interceptors.
type API struct {
settingEngine *SettingEngine
mediaEngine *MediaEngine
interceptorRegistry *interceptor.Registry
interceptor interceptor.Interceptor // Generated per PeerConnection
}
// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
//
// It uses the default Codecs and Interceptors unless you customize them
// using WithMediaEngine and WithInterceptorRegistry respectively.
func NewAPI(options ...func(*API)) *API {
api := &API{
interceptor: &interceptor.NoOp{},
settingEngine: &SettingEngine{},
}
for _, o := range options {
o(api)
}
if api.settingEngine.LoggerFactory == nil {
api.settingEngine.LoggerFactory = logging.NewDefaultLoggerFactory()
}
logger := api.settingEngine.LoggerFactory.NewLogger("api")
if api.mediaEngine == nil {
api.mediaEngine = &MediaEngine{}
err := api.mediaEngine.RegisterDefaultCodecs()
if err != nil {
logger.Errorf("Failed to register default codecs %s", err)
}
}
if api.interceptorRegistry == nil {
api.interceptorRegistry = &interceptor.Registry{}
err := RegisterDefaultInterceptorsWithOptions(api.mediaEngine, api.interceptorRegistry,
WithInterceptorLoggerFactory(api.settingEngine.LoggerFactory))
if err != nil {
logger.Errorf("Failed to register default interceptors %s", err)
}
}
return api
}
// WithMediaEngine allows providing a MediaEngine to the API.
// Settings can be changed after passing the engine to an API.
// When a PeerConnection is created the MediaEngine is copied
// and no more changes can be made.
func WithMediaEngine(m *MediaEngine) func(a *API) {
return func(a *API) {
a.mediaEngine = m
if a.mediaEngine == nil {
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(ir *interceptor.Registry) func(a *API) {
return func(a *API) {
a.interceptorRegistry = ir
if a.interceptorRegistry == nil {
a.interceptorRegistry = &interceptor.Registry{}
}
}
}
================================================
FILE: api_js.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build js && wasm
// +build js,wasm
package webrtc
// API bundles the global functions 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
}
}
================================================
FILE: api_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewAPI(t *testing.T) {
api := NewAPI()
assert.NotNil(t, api.settingEngine, "failed to init settings engine")
assert.NotNil(t, api.mediaEngine, "failed to init media engine")
assert.NotNil(t, api.interceptorRegistry, "failed to init interceptor registry")
}
func TestNewAPI_Options(t *testing.T) {
s := SettingEngine{}
s.DetachDataChannels()
api := NewAPI(
WithSettingEngine(s),
)
assert.True(t, api.settingEngine.detach.DataChannels, "failed to set settings engine")
assert.NotEmpty(t, api.mediaEngine.audioCodecs, "failed to set audio codecs")
assert.NotEmpty(t, api.mediaEngine.videoCodecs, "failed to set video codecs")
}
func TestNewAPI_OptionsDefaultize(t *testing.T) {
api := NewAPI(
WithMediaEngine(nil),
WithInterceptorRegistry(nil),
)
assert.NotNil(t, api.settingEngine)
assert.NotNil(t, api.mediaEngine)
assert.NotNil(t, api.interceptorRegistry)
}
================================================
FILE: bundlepolicy.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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 (
// BundlePolicyUnknown is the enum's zero-value.
BundlePolicyUnknown BundlePolicy = iota
// 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
// 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 BundlePolicyUnknown
}
}
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())
}
================================================
FILE: bundlepolicy_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewBundlePolicy(t *testing.T) {
testCases := []struct {
policyString string
expectedPolicy BundlePolicy
}{
{ErrUnknownType.Error(), BundlePolicyUnknown},
{"balanced", BundlePolicyBalanced},
{"max-compat", BundlePolicyMaxCompat},
{"max-bundle", BundlePolicyMaxBundle},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedPolicy,
newBundlePolicy(testCase.policyString),
"testCase: %d %v", i, testCase,
)
}
}
func TestBundlePolicy_String(t *testing.T) {
testCases := []struct {
policy BundlePolicy
expectedString string
}{
{BundlePolicyUnknown, ErrUnknownType.Error()},
{BundlePolicyBalanced, "balanced"},
{BundlePolicyMaxCompat, "max-compat"},
{BundlePolicyMaxBundle, "max-bundle"},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedString,
testCase.policy.String(),
"testCase: %d %v", i, testCase,
)
}
}
================================================
FILE: certificate.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/pem"
"fmt"
"math/big"
"strings"
"time"
"github.com/pion/dtls/v3/pkg/crypto/fingerprint"
"github.com/pion/webrtc/v4/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(cert Certificate) bool {
switch cSK := c.privateKey.(type) {
case *rsa.PrivateKey:
if oSK, ok := cert.privateKey.(*rsa.PrivateKey); ok {
if cSK.N.Cmp(oSK.N) != 0 {
return false
}
return c.x509Cert.Equal(cert.x509Cert)
}
return false
case *ecdsa.PrivateKey:
if oSK, ok := cert.privateKey.(*ecdsa.PrivateKey); ok {
if cSK.X.Cmp(oSK.X) != 0 || cSK.Y.Cmp(oSK.Y) != 0 {
return false
}
return c.x509Cert.Equal(cert.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 {
// nolint
return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err)
}
value, err := fingerprint.Fingerprint(c.x509Cert, algo)
if err != nil {
// nolint
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) {
// 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{
Issuer: pkix.Name{CommonName: generatedCertificateOrigin},
NotBefore: time.Now().AddDate(0, 0, -1),
NotAfter: time.Now().AddDate(0, 1, -1),
SerialNumber: serialNumber,
Version: 2,
Subject: pkix.Name{CommonName: generatedCertificateOrigin},
})
}
// 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
}
// CertificateFromPEM creates a fresh certificate based on a string containing
// pem blocks fort the private key and x509 certificate.
func CertificateFromPEM(pems string) (*Certificate, error) { //nolint: cyclop
var cert *x509.Certificate
var privateKey crypto.PrivateKey
var block *pem.Block
more := []byte(pems)
for {
var err error
block, more = pem.Decode(more)
if block == nil {
break
}
// decode & parse the certificate
switch block.Type {
case "CERTIFICATE":
if cert != nil {
return nil, errCertificatePEMMultipleCert
}
cert, err = x509.ParseCertificate(block.Bytes)
// If parsing failed using block.Bytes, then parse the bytes as base64 and try again
if err != nil {
var n int
certBytes := make([]byte, base64.StdEncoding.DecodedLen(len(block.Bytes)))
n, err = base64.StdEncoding.Decode(certBytes, block.Bytes)
if err == nil {
cert, err = x509.ParseCertificate(certBytes[:n])
}
}
case "PRIVATE KEY":
if privateKey != nil {
return nil, errCertificatePEMMultiplePriv
}
privateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
}
// Report errors from parsing either the private key or the certificate
if err != nil {
return nil, fmt.Errorf("failed to decode %s: %w", block.Type, err)
}
}
if cert == nil || privateKey == nil {
return nil, errCertificatePEMMissing
}
ret := CertificateFromX509(privateKey, cert)
return &ret, nil
}
// PEM returns the certificate encoded as two pem block: once for the X509
// certificate and the other for the private key.
func (c Certificate) PEM() (string, error) {
// First write the X509 certificate
var builder strings.Builder
err := pem.Encode(&builder, &pem.Block{Type: "CERTIFICATE", Bytes: c.x509Cert.Raw})
if err != nil {
return "", fmt.Errorf("failed to pem encode the X certificate: %w", err)
}
// Next write the private key
privBytes, err := x509.MarshalPKCS8PrivateKey(c.privateKey)
if err != nil {
return "", fmt.Errorf("failed to marshal private key: %w", err)
}
err = pem.Encode(&builder, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
if err != nil {
return "", fmt.Errorf("failed to encode private key: %w", err)
}
return builder.String(), nil
}
================================================
FILE: certificate_js_test.go
================================================
//go:build js && wasm
// +build js,wasm
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestGenerateCertificateRSA(t *testing.T) {
sk, err := rsa.GenerateKey(rand.Reader, 2048)
assert.Nil(t, err)
skPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(sk),
})
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.x509Cert.Raw,
})
_, err = tls.X509KeyPair(certPEM, skPEM)
assert.Nil(t, err)
}
func TestGenerateCertificateECDSA(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
skDER, err := x509.MarshalECPrivateKey(sk)
assert.Nil(t, err)
skPEM := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: skDER,
})
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.x509Cert.Raw,
})
_, err = tls.X509KeyPair(certPEM, skPEM)
assert.Nil(t, err)
}
func TestGenerateCertificateEqual(t *testing.T) {
sk1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
sk3, err := rsa.GenerateKey(rand.Reader, 2048)
assert.NoError(t, err)
cert1, err := GenerateCertificate(sk1)
assert.Nil(t, err)
sk2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert2, err := GenerateCertificate(sk2)
assert.Nil(t, err)
cert3, err := GenerateCertificate(sk3)
assert.NoError(t, err)
assert.True(t, cert1.Equals(*cert1))
assert.False(t, cert1.Equals(*cert2))
assert.True(t, cert3.Equals(*cert3))
}
func TestGenerateCertificateExpires(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
now := time.Now()
assert.False(t, cert.Expires().IsZero() || now.After(cert.Expires()))
x509Cert := CertificateFromX509(sk, &x509.Certificate{})
assert.NotNil(t, x509Cert)
assert.Contains(t, x509Cert.statsID, "certificate")
}
func TestPEM(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
pem, err := cert.PEM()
assert.Nil(t, err)
cert2, err := CertificateFromPEM(pem)
assert.Nil(t, err)
pem2, err := cert2.PEM()
assert.Nil(t, err)
assert.Equal(t, pem, pem2)
}
const (
certHeader = `!! This is a test certificate: Don't use it in production !!
You can create your own using openssl
` + "```sh" + `
openssl req -new -sha256 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 ` +
`-x509 -nodes -days 365 -out cert.pem -keyout cert.pem -subj "/CN=WebRTC"
openssl x509 -in cert.pem -noout -fingerprint -sha256
` + "```\n"
certPriv = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2XFaTNqFpTUqNtG9
A21MEe04JtsWVpUTDD8nI0KvchKhRANCAAS1nqME3jS5GFicwYfGDYaz7oSINwWm
X4BkfsSCxMrhr7mPtfxOi4Lxy/P3w6EvSSEU8t5E9ouKIWh5xPS9dYwu
-----END PRIVATE KEY-----
`
certCert = `-----BEGIN CERTIFICATE-----
MIIBljCCATugAwIBAgIUQa1sD+5HG43K+hCEVZLYxB68/hQwCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAwwVc3dpdGNoLmV2YW4tYnJhc3MubmV0MB4XDTI0MDQyNDIwMjEy
MFoXDTI1MDQyNDIwMjEyMFowIDEeMBwGA1UEAwwVc3dpdGNoLmV2YW4tYnJhc3Mu
bmV0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtZ6jBN40uRhYnMGHxg2Gs+6E
iDcFpl+AZH7EgsTK4a+5j7X8TouC8cvz98OhL0khFPLeRPaLiiFoecT0vXWMLqNT
MFEwHQYDVR0OBBYEFGecfGnYqZFVgUApHGgX2kSIhUusMB8GA1UdIwQYMBaAFGec
fGnYqZFVgUApHGgX2kSIhUusMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID
SQAwRgIhAJ3VWO8JZ7FEOJhxpUCeyOgl+G4vXSHtj9J9NRD3uGGZAiEAsTKGLOGE
9c6CtLDU9Ohf1c+Xj2Yi9H+srLZj1mrsnd4=
-----END CERTIFICATE-----
`
)
func TestOpensslCert(t *testing.T) {
// Check that CertificateFromPEM can parse certificates with the PRIVATE KEY before the CERTIFICATE block
_, err := CertificateFromPEM(certHeader + certPriv + certCert)
assert.Nil(t, err)
}
func TestEmpty(t *testing.T) {
cert, err := CertificateFromPEM("")
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMissing, err)
}
func TestMultiCert(t *testing.T) {
cert, err := CertificateFromPEM(certHeader + certCert + certPriv + certCert)
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMultipleCert, err)
}
func TestMultiPriv(t *testing.T) {
cert, err := CertificateFromPEM(certPriv + certHeader + certCert + certPriv)
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMultiplePriv, err)
}
================================================
FILE: certificate_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestGenerateCertificateRSA(t *testing.T) {
sk, err := rsa.GenerateKey(rand.Reader, 2048)
assert.Nil(t, err)
skPEM := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(sk),
})
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.x509Cert.Raw,
})
_, err = tls.X509KeyPair(certPEM, skPEM)
assert.Nil(t, err)
}
func TestGenerateCertificateECDSA(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
skDER, err := x509.MarshalECPrivateKey(sk)
assert.Nil(t, err)
skPEM := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: skDER,
})
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
certPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.x509Cert.Raw,
})
_, err = tls.X509KeyPair(certPEM, skPEM)
assert.Nil(t, err)
}
func TestGenerateCertificateEqual(t *testing.T) {
sk1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
sk3, err := rsa.GenerateKey(rand.Reader, 2048)
assert.NoError(t, err)
cert1, err := GenerateCertificate(sk1)
assert.Nil(t, err)
sk2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert2, err := GenerateCertificate(sk2)
assert.Nil(t, err)
cert3, err := GenerateCertificate(sk3)
assert.NoError(t, err)
assert.True(t, cert1.Equals(*cert1))
assert.False(t, cert1.Equals(*cert2))
assert.True(t, cert3.Equals(*cert3))
}
func TestGenerateCertificateExpires(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
now := time.Now()
assert.False(t, cert.Expires().IsZero() || now.After(cert.Expires()))
x509Cert := CertificateFromX509(sk, &x509.Certificate{})
assert.NotNil(t, x509Cert)
assert.Contains(t, x509Cert.statsID, "certificate")
}
func TestPEM(t *testing.T) {
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
cert, err := GenerateCertificate(sk)
assert.Nil(t, err)
pem, err := cert.PEM()
assert.Nil(t, err)
cert2, err := CertificateFromPEM(pem)
assert.Nil(t, err)
pem2, err := cert2.PEM()
assert.Nil(t, err)
assert.Equal(t, pem, pem2)
}
const (
certHeader = `!! This is a test certificate: Don't use it in production !!
You can create your own using openssl
` + "```sh" + `
openssl req -new -sha256 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 ` +
`-x509 -nodes -days 365 -out cert.pem -keyout cert.pem -subj "/CN=WebRTC"
openssl x509 -in cert.pem -noout -fingerprint -sha256
` + "```\n"
certPriv = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2XFaTNqFpTUqNtG9
A21MEe04JtsWVpUTDD8nI0KvchKhRANCAAS1nqME3jS5GFicwYfGDYaz7oSINwWm
X4BkfsSCxMrhr7mPtfxOi4Lxy/P3w6EvSSEU8t5E9ouKIWh5xPS9dYwu
-----END PRIVATE KEY-----
`
certCert = `-----BEGIN CERTIFICATE-----
MIIBljCCATugAwIBAgIUQa1sD+5HG43K+hCEVZLYxB68/hQwCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAwwVc3dpdGNoLmV2YW4tYnJhc3MubmV0MB4XDTI0MDQyNDIwMjEy
MFoXDTI1MDQyNDIwMjEyMFowIDEeMBwGA1UEAwwVc3dpdGNoLmV2YW4tYnJhc3Mu
bmV0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtZ6jBN40uRhYnMGHxg2Gs+6E
iDcFpl+AZH7EgsTK4a+5j7X8TouC8cvz98OhL0khFPLeRPaLiiFoecT0vXWMLqNT
MFEwHQYDVR0OBBYEFGecfGnYqZFVgUApHGgX2kSIhUusMB8GA1UdIwQYMBaAFGec
fGnYqZFVgUApHGgX2kSIhUusMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID
SQAwRgIhAJ3VWO8JZ7FEOJhxpUCeyOgl+G4vXSHtj9J9NRD3uGGZAiEAsTKGLOGE
9c6CtLDU9Ohf1c+Xj2Yi9H+srLZj1mrsnd4=
-----END CERTIFICATE-----
`
)
func TestOpensslCert(t *testing.T) {
// Check that CertificateFromPEM can parse certificates with the PRIVATE KEY before the CERTIFICATE block
_, err := CertificateFromPEM(certHeader + certPriv + certCert)
assert.Nil(t, err)
}
func TestEmpty(t *testing.T) {
cert, err := CertificateFromPEM("")
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMissing, err)
}
func TestMultiCert(t *testing.T) {
cert, err := CertificateFromPEM(certHeader + certCert + certPriv + certCert)
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMultipleCert, err)
}
func TestMultiPriv(t *testing.T) {
cert, err := CertificateFromPEM(certPriv + certHeader + certCert + certPriv)
assert.Nil(t, cert)
assert.Equal(t, errCertificatePEMMultiplePriv, err)
}
================================================
FILE: codecov.yml
================================================
#
# DO NOT EDIT THIS FILE
#
# It is automatically copied from https://github.com/pion/.goassets repository.
#
# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
# SPDX-License-Identifier: MIT
coverage:
status:
project:
default:
# Allow decreasing 2% of total coverage to avoid noise.
threshold: 2%
patch:
default:
target: 70%
only_pulls: true
ignore:
- "examples/*"
- "examples/**/*"
================================================
FILE: configuration.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go: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,omitempty"`
// 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"`
// AlwaysNegotiateDataChannels specifies whether the application prefers
// to always negotiate data channels in the initial SDP offer.
AlwaysNegotiateDataChannels bool `json:"alwaysNegotiateDataChannels,omitempty"`
}
================================================
FILE: configuration_common.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}
================================================
FILE: configuration_js.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build js && wasm
// +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
// AlwaysNegotiateDataChannels specifies whether the application prefers
// to always negotiate data channels in the initial SDP offer.
AlwaysNegotiateDataChannels bool
Certificates []Certificate `json:"certificates,omitempty"`
}
================================================
FILE: configuration_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfiguration_getICEServers(t *testing.T) {
t.Run("Success", func(t *testing.T) {
expectedServerStr := "stun:stun.l.google.com:19302"
cfg := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{expectedServerStr},
},
},
}
parsedURLs := cfg.getICEServers()
assert.Equal(t, expectedServerStr, parsedURLs[0].URLs[0])
})
t.Run("Success", func(t *testing.T) {
// ignore the fact that stun URLs shouldn't have a query
serverStr := "stun:global.stun.twilio.com:3478?transport=udp"
expectedServerStr := "stun:global.stun.twilio.com:3478"
cfg := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{serverStr},
},
},
}
parsedURLs := cfg.getICEServers()
assert.Equal(t, expectedServerStr, parsedURLs[0].URLs[0])
})
}
func TestConfigurationJSON(t *testing.T) {
config := `{
"iceServers": [{"urls": ["turn:turn.example.org"],
"username": "jch",
"credential": "topsecret"
}],
"iceTransportPolicy": "relay",
"bundlePolicy": "balanced",
"rtcpMuxPolicy": "require"
}`
conf := Configuration{
ICEServers: []ICEServer{
{
URLs: []string{"turn:turn.example.org"},
Username: "jch",
Credential: "topsecret",
},
},
ICETransportPolicy: ICETransportPolicyRelay,
BundlePolicy: BundlePolicyBalanced,
RTCPMuxPolicy: RTCPMuxPolicyRequire,
}
var conf2 Configuration
assert.NoError(t, json.Unmarshal([]byte(config), &conf2))
assert.Equal(t, conf, conf2)
j2, err := json.Marshal(conf2)
assert.NoError(t, err)
var conf3 Configuration
assert.NoError(t, json.Unmarshal(j2, &conf3))
assert.Equal(t, conf2, conf3)
}
================================================
FILE: constants.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"math"
"github.com/pion/dtls/v3"
)
const (
// default as the standard ethernet MTU
// can be overwritten with SettingEngine.SetReceiveMTU().
receiveMTU = 1500
// 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
// Default Max SCTP Message Size is the largest single DataChannel
// message we can send or accept. This default was chosen to match FireFox.
defaultMaxSCTPMessageSize = 1073741823
// If a DataChannel Max Message Size isn't declared by the Remote(max-message-size)
// this is the value we default to. This value was chosen because it was the behavior
// of Pion before max-message-size was implemented.
sctpMaxMessageSizeUnsetValue = math.MaxUint16
mediaSectionApplication = "application"
sdpAttributeRid = "rid"
sdpAttributeSimulcast = "simulcast"
outboundMTU = 1200
rtpPayloadTypeBitmask = 0x7F
incomingUnhandledRTPSsrc = "Incoming unhandled RTP ssrc(%d), OnTrack will not be fired. %v"
useReadSimulcast = "Use ReadSimulcast(rid) instead of Read() when multiple tracks are present"
generatedCertificateOrigin = "WebRTC"
// AttributeRtxPayloadType is the interceptor attribute added when Read()
// returns an RTX packet containing the RTX stream payload type.
AttributeRtxPayloadType = "rtx_payload_type"
// AttributeRtxSsrc is the interceptor attribute added when Read()
// returns an RTX packet containing the RTX stream SSRC.
AttributeRtxSsrc = "rtx_ssrc"
// AttributeRtxSequenceNumber is the interceptor attribute added when
// Read() returns an RTX packet containing the RTX stream sequence number.
AttributeRtxSequenceNumber = "rtx_sequence_number"
)
func defaultSrtpProtectionProfiles() []dtls.SRTPProtectionProfile {
return []dtls.SRTPProtectionProfile{
dtls.SRTP_AEAD_AES_256_GCM,
dtls.SRTP_AEAD_AES_128_GCM,
dtls.SRTP_AES128_CM_HMAC_SHA1_80,
}
}
================================================
FILE: datachannel.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
"github.com/pion/datachannel"
"github.com/pion/logging"
"github.com/pion/webrtc/v4/pkg/rtcerr"
)
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
readLoopActive chan struct{}
isGracefulClosed 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()
dialHandlerOnce sync.Once
onDialHandler 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, nil, 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,
sctpTransport *SCTPTransport,
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}
}
dataChannel := &DataChannel{
sctpTransport: sctpTransport,
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,
}
dataChannel.setReadyState(DataChannelStateConnecting)
return dataChannel, nil
}
// open opens the datachannel over the sctp transport.
func (d *DataChannel) open(sctpTransport *SCTPTransport) error { //nolint:cyclop
association := sctpTransport.association()
if association == nil {
return errSCTPNotEstablished
}
d.mu.Lock()
if d.sctpTransport != nil { // already open
d.mu.Unlock()
return nil
}
d.sctpTransport = sctpTransport
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 {
// avoid holding lock when generating ID, since id generation locks
d.mu.Unlock()
var dcID *uint16
err := d.sctpTransport.generateAndSetDataChannelID(d.sctpTransport.dtlsTransport.role(), &dcID)
if err != nil {
return err
}
d.mu.Lock()
d.id = dcID
}
dc, err := datachannel.Dial(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.onDial()
d.handleOpen(dc, false, d.negotiated)
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
if d.isGracefulClosed {
d.mu.RUnlock()
return
}
d.mu.RUnlock()
if handler != nil {
go d.openHandlerOnce.Do(func() {
handler()
d.checkDetachAfterOpen()
})
}
}
// OnDial sets an event handler which is invoked when the
// peer has been dialed, but before said peer has responded.
func (d *DataChannel) OnDial(f func()) {
d.mu.Lock()
d.dialHandlerOnce = sync.Once{}
d.onDialHandler = f
d.mu.Unlock()
if d.ReadyState() == DataChannelStateOpen {
// If the data channel is already open, call the handler immediately.
go d.dialHandlerOnce.Do(f)
}
}
func (d *DataChannel) onDial() {
d.mu.RLock()
handler := d.onDialHandler
if d.isGracefulClosed {
d.mu.RUnlock()
return
}
d.mu.RUnlock()
if handler != nil {
go d.dialHandlerOnce.Do(handler)
}
}
// OnClose sets an event handler which is invoked when
// the underlying data transport has been closed.
// Note: Due to backwards compatibility, there is a chance that
// OnClose can be called, even if the GracefulClose is used.
// If this is the case for you, you can deregister OnClose
// prior to GracefulClose.
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
if d.isGracefulClosed {
d.mu.RUnlock()
return
}
d.mu.RUnlock()
if handler == nil {
return
}
handler(msg)
}
func (d *DataChannel) handleOpen(dc *datachannel.DataChannel, isRemote, isAlreadyNegotiated bool) {
d.mu.Lock()
if d.isGracefulClosed { // The channel was closed during the connecting state
d.mu.Unlock()
if err := dc.Close(); err != nil {
d.log.Errorf("Failed to close DataChannel that was closed during connecting state %v", err.Error())
}
d.onClose()
return
}
d.dataChannel = dc
bufferedAmountLowThreshold := d.bufferedAmountLowThreshold
onBufferedAmountLow := d.onBufferedAmountLow
d.mu.Unlock()
d.setReadyState(DataChannelStateOpen)
// Fire the OnOpen handler immediately not using pion/datachannel
// * detached datachannels have no read loop, the user needs to read and query themselves
// * remote datachannels should fire OnOpened. This isn't spec compliant, but we can't break behavior yet
// * already negotiated datachannels should fire OnOpened
if d.api.settingEngine.detach.DataChannels || isRemote || isAlreadyNegotiated {
// bufferedAmountLowThreshold and onBufferedAmountLow might be set earlier
d.dataChannel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold)
d.dataChannel.OnBufferedAmountLow(onBufferedAmountLow)
d.onOpen()
} else {
dc.OnOpen(func() {
d.onOpen()
})
}
d.mu.Lock()
defer d.mu.Unlock()
if d.isGracefulClosed {
return
}
if !d.api.settingEngine.detach.DataChannels {
d.readLoopActive = make(chan struct{})
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
if d.isGracefulClosed {
d.mu.RUnlock()
return
}
d.mu.RUnlock()
if handler != nil {
go handler(err)
}
}
func (d *DataChannel) readLoop() {
defer func() {
d.mu.Lock()
readLoopActive := d.readLoopActive
d.mu.Unlock()
defer close(readLoopActive)
}()
buffer := make([]byte, sctpMaxMessageSizeUnsetValue)
for {
n, isString, err := d.dataChannel.ReadDataChannel(buffer)
if err != nil {
if errors.Is(err, io.ErrShortBuffer) {
if int64(n) < int64(d.api.settingEngine.getSCTPMaxMessageSize()) {
buffer = append(buffer, make([]byte, len(buffer))...) // nolint
continue
}
d.log.Errorf(
"Incoming DataChannel message larger then Max Message size %v",
d.api.settingEngine.getSCTPMaxMessageSize(),
)
}
d.setReadyState(DataChannelStateClosed)
if !errors.Is(err, io.EOF) {
d.onError(err)
}
d.onClose()
return
}
d.onMessage(DataChannelMessage{
Data: append([]byte{}, buffer[:n]...),
IsString: isString,
})
}
}
// 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
// (`io.ReadWriteCloser` with its `.Read()` and `.Write()` methods,
// as opposed to `.Send()` and `.OnMessage`),
// 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) {
return d.DetachWithDeadline()
}
// DetachWithDeadline allows you to detach the underlying datachannel.
// It is the same as Detach but returns a ReadWriteCloserDeadliner.
func (d *DataChannel) DetachWithDeadline() (datachannel.ReadWriteCloserDeadliner, error) {
d.mu.Lock()
if !d.api.settingEngine.detach.DataChannels {
d.mu.Unlock()
return nil, errDetachNotEnabled
}
if d.dataChannel == nil {
d.mu.Unlock()
return nil, errDetachBeforeOpened
}
d.detachCalled = true
dataChannel := d.dataChannel
d.mu.Unlock()
// Remove the reference from SCTPTransport so that the datachannel
// can be garbage collected on close
d.sctpTransport.lock.Lock()
n := len(d.sctpTransport.dataChannels)
j := 0
for i := range n {
if d == d.sctpTransport.dataChannels[i] {
continue
}
d.sctpTransport.dataChannels[j] = d.sctpTransport.dataChannels[i]
j++
}
for i := j; i < n; i++ {
d.sctpTransport.dataChannels[i] = nil
}
d.sctpTransport.dataChannels = d.sctpTransport.dataChannels[:j]
d.sctpTransport.lock.Unlock()
return 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 {
return d.close(false)
}
// GracefulClose Closes the DataChannel. It may be called regardless of whether
// the DataChannel object was created by this peer or the remote peer. It also waits
// for any goroutines it started to complete. This is only safe to call outside of
// DataChannel callbacks or if in a callback, in its own goroutine.
func (d *DataChannel) GracefulClose() error {
return d.close(true)
}
// Normally, close only stops writes from happening, so graceful=true
// will wait for reads to be finished based on underlying SCTP association
// closure or a SCTP reset stream from the other side. This is safe to call
// with graceful=true after tearing down a PeerConnection but not
// necessarily before. For example, if you used a vnet and dropped all packets
// right before closing the DataChannel, you'd need never see a reset stream.
func (d *DataChannel) close(shouldGracefullyClose bool) error {
d.mu.Lock()
d.isGracefulClosed = true
readLoopActive := d.readLoopActive
if shouldGracefullyClose && readLoopActive != nil {
defer func() {
<-readLoopActive
}()
}
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 returns true 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, ok := d.readyState.Load().(DataChannelState); ok {
return v
}
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 or equal to the
// BufferedAmountLowThreshold.
func (d *DataChannel) OnBufferedAmountLow(f func()) {
d.mu.Lock()
defer d.mu.Unlock()
onBufferedAmountLow := d.makeBufferedAmountLowHandler(f)
d.onBufferedAmountLow = onBufferedAmountLow
if d.dataChannel != nil {
d.dataChannel.OnBufferedAmountLow(onBufferedAmountLow)
}
}
func (d *DataChannel) makeBufferedAmountLowHandler(f func()) func() {
return func() {
go func() {
if d.ReadyState() != DataChannelStateOpen {
return
}
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)
}
================================================
FILE: datachannel_go_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"context"
"crypto/rand"
"encoding/binary"
"io"
"math/big"
"regexp"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/pion/datachannel"
"github.com/pion/logging"
"github.com/pion/transport/v4/test"
"github.com/stretchr/testify/assert"
)
func TestDataChannel_EventHandlers(t *testing.T) {
to := test.TimeOut(time.Second * 20)
defer to.Stop()
report := test.CheckRoutines(t)
defer report()
api := NewAPI()
dc := &DataChannel{api: api}
onDialCalled := make(chan struct{})
onOpenCalled := make(chan struct{})
onMessageCalled := make(chan struct{})
// Verify that the noop case works
assert.NotPanics(t, func() { dc.onOpen() })
dc.OnDial(func() {
close(onDialCalled)
})
dc.OnOpen(func() {
close(onOpenCalled)
})
dc.OnMessage(func(DataChannelMessage) {
close(onMessageCalled)
})
// Verify that the set handlers are called
assert.NotPanics(t, func() { dc.onDial() })
assert.NotPanics(t, func() { dc.onOpen() })
assert.NotPanics(t, func() { dc.onMessage(DataChannelMessage{Data: []byte("o hai")}) })
// Wait for all handlers to be called
<-onDialCalled
<-onOpenCalled
<-onMessageCalled
}
func TestDataChannel_MessagesAreOrdered(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
api := NewAPI()
dc := &DataChannel{api: api}
maxVal := 512
out := make(chan int)
inner := func(msg DataChannelMessage) {
// randomly sleep
// math/rand a weak RNG, but this does not need to be secure. Ignore with #nosec
/* #nosec */
randInt, err := rand.Int(rand.Reader, big.NewInt(int64(maxVal)))
assert.NoError(t, err, "Failed to get random sleep duration")
time.Sleep(time.Duration(randInt.Int64()) * time.Microsecond)
s, _ := binary.Varint(msg.Data)
out <- int(s)
}
dc.OnMessage(func(p DataChannelMessage) {
inner(p)
})
go func() {
for i := 1; i <= maxVal; i++ {
buf := make([]byte, 8)
binary.PutVarint(buf, int64(i))
dc.onMessage(DataChannelMessage{Data: buf})
// Change the registered handler a couple of times to make sure
// that everything continues to work, we don't lose messages, etc.
if i%2 == 0 {
handler := func(msg DataChannelMessage) {
inner(msg)
}
dc.OnMessage(handler)
}
}
}()
values := make([]int, 0, maxVal)
for v := range out {
values = append(values, v)
if len(values) == maxVal {
close(out)
}
}
expected := make([]int, maxVal)
for i := 1; i <= maxVal; i++ {
expected[i-1] = i
}
assert.EqualValues(t, expected, values)
}
// Note(albrow): This test includes some features that aren't supported by the
// Wasm bindings (at least for now).
func TestDataChannelParamters_Go(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
ordered := true
var maxPacketLifeTime uint16 = 3
options := &DataChannelInit{
Ordered: &ordered,
MaxPacketLifeTime: &maxPacketLifeTime,
}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
// Check if parameters are correctly set
assert.True(t, dc.Ordered(), "Ordered should be set to true")
if assert.NotNil(t, dc.MaxPacketLifeTime(), "should not be nil") {
assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime(), "should match")
}
answerPC.OnDataChannel(func(d *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if d.Label() != expectedLabel {
return
}
// Check if parameters are correctly set
assert.True(t, d.ordered, "Ordered should be set to true")
if assert.NotNil(t, d.maxPacketLifeTime, "should not be nil") {
assert.Equal(t, maxPacketLifeTime, *d.maxPacketLifeTime, "should match")
}
done <- true
})
closeReliabilityParamTest(t, offerPC, answerPC, done)
})
t.Run("All other property methods", func(t *testing.T) {
id := uint16(123)
dc := &DataChannel{}
dc.id = &id
dc.label = "mylabel"
dc.protocol = "myprotocol"
dc.negotiated = true
assert.Equal(t, dc.id, dc.ID(), "should match")
assert.Equal(t, dc.label, dc.Label(), "should match")
assert.Equal(t, dc.protocol, dc.Protocol(), "should match")
assert.Equal(t, dc.negotiated, dc.Negotiated(), "should match")
assert.Equal(t, uint64(0), dc.BufferedAmount(), "should match")
dc.SetBufferedAmountLowThreshold(1500)
assert.Equal(t, uint64(1500), dc.BufferedAmountLowThreshold(), "should match")
})
}
func TestDataChannelBufferedAmount(t *testing.T) { //nolint:cyclop
t.Run("set before datachannel becomes open", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
var nOfferBufferedAmountLowCbs uint32
var offerBufferedAmountLowThreshold uint64 = 1500
var nAnswerBufferedAmountLowCbs uint32
var answerBufferedAmountLowThreshold uint64 = 1400
buf := make([]byte, 1000)
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
nPacketsToSend := int(10)
var nOfferReceived uint32
var nAnswerReceived uint32
done := make(chan bool)
answerPC.OnDataChannel(func(answerDC *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if answerDC.Label() != expectedLabel {
return
}
answerDC.OnOpen(func() {
assert.Equal(t, answerBufferedAmountLowThreshold, answerDC.BufferedAmountLowThreshold(), "value mismatch")
for range nPacketsToSend {
e := answerDC.Send(buf)
assert.NoError(t, e, "Failed to send string on data channel")
}
})
answerDC.OnMessage(func(DataChannelMessage) {
atomic.AddUint32(&nAnswerReceived, 1)
})
assert.True(t, answerDC.Ordered(), "Ordered should be set to true")
// The value is temporarily stored in the answerDC object
// until the answerDC gets opened
answerDC.SetBufferedAmountLowThreshold(answerBufferedAmountLowThreshold)
// The callback function is temporarily stored in the answerDC object
// until the answerDC gets opened
answerDC.OnBufferedAmountLow(func() {
atomic.AddUint32(&nAnswerBufferedAmountLowCbs, 1)
if atomic.LoadUint32(&nOfferBufferedAmountLowCbs) > 0 {
done <- true
}
})
})
offerDC, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err, "Failed to create a PC pair for testing")
assert.True(t, offerDC.Ordered(), "Ordered should be set to true")
offerDC.OnOpen(func() {
assert.Equal(t, offerBufferedAmountLowThreshold, offerDC.BufferedAmountLowThreshold(), "value mismatch")
for range nPacketsToSend {
e := offerDC.Send(buf)
assert.NoError(t, e, "Failed to send string on data channel")
// assert.Equal(t, (i+1)*len(buf), int(offerDC.BufferedAmount()), "unexpected bufferedAmount")
}
})
offerDC.OnMessage(func(DataChannelMessage) {
atomic.AddUint32(&nOfferReceived, 1)
})
// The value is temporarily stored in the offerDC object
// until the offerDC gets opened
offerDC.SetBufferedAmountLowThreshold(offerBufferedAmountLowThreshold)
// The callback function is temporarily stored in the offerDC object
// until the offerDC gets opened
offerDC.OnBufferedAmountLow(func() {
atomic.AddUint32(&nOfferBufferedAmountLowCbs, 1)
if atomic.LoadUint32(&nAnswerBufferedAmountLowCbs) > 0 {
done <- true
}
})
err = signalPair(offerPC, answerPC)
assert.NoError(t, err, "Failed to signal our PC pair for testing")
closePair(t, offerPC, answerPC, done)
t.Logf("nOfferBufferedAmountLowCbs : %d", nOfferBufferedAmountLowCbs)
t.Logf("nAnswerBufferedAmountLowCbs: %d", nAnswerBufferedAmountLowCbs)
assert.True(t, nOfferBufferedAmountLowCbs > uint32(0), "callback should be made at least once")
assert.True(t, nAnswerBufferedAmountLowCbs > uint32(0), "callback should be made at least once")
})
t.Run("set after datachannel becomes open", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
var nCbs uint32
buf := make([]byte, 1000)
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
answerPC.OnDataChannel(func(dataChannel *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if dataChannel.Label() != expectedLabel {
return
}
var nPacketsReceived int
dataChannel.OnMessage(func(DataChannelMessage) {
nPacketsReceived++
if nPacketsReceived == 10 {
go func() {
time.Sleep(time.Second)
done <- true
}()
}
})
assert.True(t, dataChannel.Ordered(), "Ordered should be set to true")
})
dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
assert.True(t, dc.Ordered(), "Ordered should be set to true")
dc.OnOpen(func() {
// The value should directly be passed to sctp
dc.SetBufferedAmountLowThreshold(1500)
// The callback function should directly be passed to sctp
dc.OnBufferedAmountLow(func() {
atomic.AddUint32(&nCbs, 1)
})
for range 10 {
assert.NoError(t, dc.Send(buf), "Failed to send string on data channel")
assert.Equal(t, uint64(1500), dc.BufferedAmountLowThreshold(), "value mismatch")
// assert.Equal(t, (i+1)*len(buf), int(dc.BufferedAmount()), "unexpected bufferedAmount")
}
})
dc.OnMessage(func(DataChannelMessage) {
})
assert.NoError(t, signalPair(offerPC, answerPC))
closePair(t, offerPC, answerPC, done)
assert.True(t, atomic.LoadUint32(&nCbs) > 0, "callback should be made at least once")
})
}
func TestEOF(t *testing.T) { //nolint:cyclop
t.Helper()
report := test.CheckRoutines(t)
defer report()
log := logging.NewDefaultLoggerFactory().NewLogger("test")
label := "test-channel"
testData := []byte("this is some test data")
t.Run("Detach", func(t *testing.T) {
// Use Detach data channels mode
s := SettingEngine{}
s.DetachDataChannels()
api := NewAPI(WithSettingEngine(s))
// Set up two peer connections.
config := Configuration{}
pca, err := api.NewPeerConnection(config)
assert.NoError(t, err)
pcb, err := api.NewPeerConnection(config)
assert.NoError(t, err)
defer closePairNow(t, pca, pcb)
var wg sync.WaitGroup
dcChan := make(chan datachannel.ReadWriteCloser)
pcb.OnDataChannel(func(dc *DataChannel) {
if dc.Label() != label {
return
}
log.Debug("OnDataChannel was called")
dc.OnOpen(func() {
detached, err2 := dc.Detach()
assert.NoError(t, err2, "Detach failed")
dcChan <- detached
})
})
wg.Add(1)
go func() {
defer wg.Done()
var msg []byte
log.Debug("Waiting for OnDataChannel")
dc := <-dcChan
log.Debug("data channel opened")
defer func() { assert.NoError(t, dc.Close(), "should succeed") }()
log.Debug("Waiting for ping...")
msg, err2 := io.ReadAll(dc)
log.Debugf("Received ping! \"%s\"", string(msg))
assert.NoError(t, err2)
assert.Equal(t, testData, msg)
}()
assert.NoError(t, signalPair(pca, pcb))
attached, err := pca.CreateDataChannel(label, nil)
assert.NoError(t, err)
log.Debug("Waiting for data channel to open")
open := make(chan struct{})
attached.OnOpen(func() {
open <- struct{}{}
})
<-open
log.Debug("data channel opened")
var dc io.ReadWriteCloser
dc, err = attached.Detach()
assert.NoError(t, err)
wg.Add(1)
go func() {
defer wg.Done()
log.Debug("Sending ping...")
_, err = dc.Write(testData)
assert.NoError(t, err)
log.Debug("Sent ping")
assert.NoError(t, dc.Close(), "should succeed")
log.Debug("Wating for EOF")
ret, err2 := io.ReadAll(dc)
assert.Nil(t, err2, "should succeed")
assert.Equal(t, 0, len(ret), "should be empty")
}()
wg.Wait()
})
t.Run("No detach", func(t *testing.T) {
lim := test.TimeOut(time.Second * 5)
defer lim.Stop()
// Set up two peer connections.
config := Configuration{}
pca, err := NewPeerConnection(config)
assert.NoError(t, err)
pcb, err := NewPeerConnection(config)
assert.NoError(t, err)
defer closePairNow(t, pca, pcb)
var dca, dcb *DataChannel
dcaClosedCh := make(chan struct{})
dcbClosedCh := make(chan struct{})
pcb.OnDataChannel(func(dc *DataChannel) {
if dc.Label() != label {
return
}
log.Debugf("pcb: new datachannel: %s", dc.Label())
dcb = dc
// Register channel opening handling
dcb.OnOpen(func() {
log.Debug("pcb: datachannel opened")
})
dcb.OnClose(func() {
// (2)
log.Debug("pcb: data channel closed")
close(dcbClosedCh)
})
// Register the OnMessage to handle incoming messages
log.Debug("pcb: registering onMessage callback")
dcb.OnMessage(func(dcMsg DataChannelMessage) {
log.Debugf("pcb: received ping: %s", string(dcMsg.Data))
assert.Equal(t, testData, dcMsg.Data)
})
})
dca, err = pca.CreateDataChannel(label, nil)
assert.NoError(t, err)
dca.OnOpen(func() {
log.Debug("pca: data channel opened")
log.Debugf("pca: sending \"%s\"", string(testData))
assert.NoError(t, dca.Send(testData))
log.Debug("pca: sent ping")
assert.NoError(t, dca.Close(), "should succeed") // <-- dca closes
})
dca.OnClose(func() {
// (1)
log.Debug("pca: data channel closed")
close(dcaClosedCh)
})
// Register the OnMessage to handle incoming messages
log.Debug("pca: registering onMessage callback")
dca.OnMessage(func(dcMsg DataChannelMessage) {
log.Debugf("pca: received pong: %s", string(dcMsg.Data))
assert.Equal(t, testData, dcMsg.Data)
})
assert.NoError(t, signalPair(pca, pcb))
// When dca closes the channel,
// (1) dca.Onclose() will fire immediately, then
// (2) dcb.OnClose will also fire
<-dcaClosedCh // (1)
<-dcbClosedCh // (2)
})
}
// Assert that a Session Description that doesn't follow
// draft-ietf-mmusic-sctp-sdp is still accepted.
func TestDataChannel_NonStandardSessionDescription(t *testing.T) {
to := test.TimeOut(time.Second * 20)
defer to.Stop()
report := test.CheckRoutines(t)
defer report()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
_, err = offerPC.CreateDataChannel("foo", nil)
assert.NoError(t, err)
onDataChannelCalled := make(chan struct{})
answerPC.OnDataChannel(func(_ *DataChannel) {
close(onDataChannelCalled)
})
offer, err := offerPC.CreateOffer(nil)
assert.NoError(t, err)
offerGatheringComplete := GatheringCompletePromise(offerPC)
assert.NoError(t, offerPC.SetLocalDescription(offer))
<-offerGatheringComplete
offer = *offerPC.LocalDescription()
// Replace with old values
const (
oldApplication = "m=application 63743 DTLS/SCTP 5000\r"
oldAttribute = "a=sctpmap:5000 webrtc-datachannel 256\r"
)
offer.SDP = regexp.MustCompile(`m=application (.*?)\r`).ReplaceAllString(offer.SDP, oldApplication)
offer.SDP = regexp.MustCompile(`a=sctp-port(.*?)\r`).ReplaceAllString(offer.SDP, oldAttribute)
// Assert that replace worked
assert.True(t, strings.Contains(offer.SDP, oldApplication))
assert.True(t, strings.Contains(offer.SDP, oldAttribute))
assert.NoError(t, answerPC.SetRemoteDescription(offer))
answer, err := answerPC.CreateAnswer(nil)
assert.NoError(t, err)
answerGatheringComplete := GatheringCompletePromise(answerPC)
assert.NoError(t, answerPC.SetLocalDescription(answer))
<-answerGatheringComplete
assert.NoError(t, offerPC.SetRemoteDescription(*answerPC.LocalDescription()))
<-onDataChannelCalled
closePairNow(t, offerPC, answerPC)
}
func TestDataChannel_Dial(t *testing.T) {
t.Run("handler should be called once, by dialing peer only", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
dialCalls := make(chan bool, 2)
wg := new(sync.WaitGroup)
wg.Add(2)
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
answerPC.OnDataChannel(func(d *DataChannel) {
if d.Label() != expectedLabel {
return
}
d.OnDial(func() {
// only dialing side should fire OnDial
assert.Fail(t, "answering side should not call on dial")
})
d.OnOpen(wg.Done)
})
d, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
d.OnDial(func() {
dialCalls <- true
wg.Done()
})
assert.NoError(t, signalPair(offerPC, answerPC))
wg.Wait()
closePairNow(t, offerPC, answerPC)
assert.Len(t, dialCalls, 1)
})
t.Run("handler should be called immediately if already dialed", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
done := make(chan bool)
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
d, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
d.OnOpen(func() {
// when the offer DC has been opened, its guaranteed to have dialed since it has
// received a response to said dial. this test represents an unrealistic usage,
// but its the best way to guarantee we "missed" the dial event and still invoke
// the handler.
d.OnDial(func() {
done <- true
})
})
assert.NoError(t, signalPair(offerPC, answerPC))
closePair(t, offerPC, answerPC, done)
})
}
func TestDetachRemovesDatachannelReference(t *testing.T) {
// Use Detach data channels mode
s := SettingEngine{}
s.DetachDataChannels()
api := NewAPI(WithSettingEngine(s))
// Set up two peer connections.
config := Configuration{}
pca, err := api.NewPeerConnection(config)
assert.NoError(t, err)
pcb, err := api.NewPeerConnection(config)
assert.NoError(t, err)
defer closePairNow(t, pca, pcb)
dcChan := make(chan *DataChannel, 1)
pcb.OnDataChannel(func(d *DataChannel) {
d.OnOpen(func() {
_, detachErr := d.Detach()
assert.NoError(t, detachErr)
dcChan <- d
})
})
assert.NoError(t, signalPair(pca, pcb))
attached, err := pca.CreateDataChannel("", nil)
assert.NoError(t, err)
open := make(chan struct{}, 1)
attached.OnOpen(func() {
open <- struct{}{}
})
<-open
d := <-dcChan
d.sctpTransport.lock.RLock()
defer d.sctpTransport.lock.RUnlock()
for _, dc := range d.sctpTransport.dataChannels[:cap(d.sctpTransport.dataChannels)] {
assert.NotEqual(t, dc, d, "expected sctpTransport to drop reference to datachannel")
}
}
func TestDataChannelClose(t *testing.T) {
// Test if onClose is fired for self and remote after Close is called
t.Run("close open channels", func(t *testing.T) {
options := &DataChannelInit{}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
answerPC.OnDataChannel(func(dataChannel *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if dataChannel.Label() != expectedLabel {
return
}
dataChannel.OnOpen(func() {
assert.NoError(t, dataChannel.Close())
})
dataChannel.OnClose(func() {
done <- true
})
})
dc.OnClose(func() {
done <- true
})
assert.NoError(t, signalPair(offerPC, answerPC))
// Offer and Answer OnClose
<-done
<-done
assert.NoError(t, offerPC.Close())
assert.NoError(t, answerPC.Close())
})
// Test if OnClose is fired for self and remote after Close is called on non-established channel
// https://github.com/pion/webrtc/issues/2659
t.Run("Close connecting channels", func(t *testing.T) {
options := &DataChannelInit{}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
answerPC.OnDataChannel(func(dataChannel *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if dataChannel.Label() != expectedLabel {
return
}
dataChannel.OnOpen(func() {
assert.Fail(t, "OnOpen must not be fired after we call Close")
})
dataChannel.OnClose(func() {
done <- true
})
assert.NoError(t, dataChannel.Close())
})
dc.OnClose(func() {
done <- true
})
assert.NoError(t, signalPair(offerPC, answerPC))
// Offer and Answer OnClose
<-done
<-done
assert.NoError(t, offerPC.Close())
assert.NoError(t, answerPC.Close())
})
}
func TestDataChannel_DetachErrors(t *testing.T) {
t.Run("error errDetachNotEnabled", func(t *testing.T) {
s := SettingEngine{}
offer, answer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{})
assert.NoError(t, err)
dc, err := offer.CreateDataChannel("data", nil)
assert.NoError(t, err)
_, err = dc.Detach()
assert.ErrorIs(t, err, errDetachNotEnabled)
assert.NoError(t, offer.Close())
assert.NoError(t, answer.Close())
})
t.Run("error errDetachBeforeOpened", func(t *testing.T) {
s := SettingEngine{}
s.DetachDataChannels()
offer, answer, err := NewAPI(WithSettingEngine(s)).newPair(Configuration{})
assert.NoError(t, err)
dc, err := offer.CreateDataChannel("data", nil)
assert.NoError(t, err)
_, err = dc.Detach()
assert.ErrorIs(t, err, errDetachBeforeOpened)
assert.NoError(t, offer.Close())
assert.NoError(t, answer.Close())
})
}
func TestDataChannelMessageSize(t *testing.T) {
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
dc, err := offerPC.CreateDataChannel("", nil)
assert.NoError(t, err)
answerDataChannelMessages := make(chan []byte)
answerPC.OnDataChannel(func(d *DataChannel) {
d.OnMessage(func(m DataChannelMessage) {
answerDataChannelMessages <- m.Data
})
})
assert.NoError(t, signalPair(offerPC, answerPC))
messagesSent, messagesSentCancel := context.WithCancel(context.Background())
dc.OnOpen(func() {
for i := 0; i <= 10; i++ {
outboundMessage := make([]byte, sctpMaxMessageSizeUnsetValue*i)
_, err := rand.Read(outboundMessage)
assert.NoError(t, err)
assert.NoError(t, dc.Send(outboundMessage))
inboundMessage := <-answerDataChannelMessages
assert.Equal(t, outboundMessage, inboundMessage)
}
messagesSentCancel()
})
<-messagesSent.Done()
closePairNow(t, offerPC, answerPC)
}
func TestOnBufferedAmountLowDeadlock(t *testing.T) {
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
offerDataChannel, err := offerPC.CreateDataChannel("", nil)
assert.NoError(t, err)
assert.NoError(t, signalPair(offerPC, answerPC))
gotAllMessages, gotAllMessagesCancel := context.WithCancel(context.Background())
offerDataChannel.OnOpen(func() {
for {
select {
case <-gotAllMessages.Done():
return
case <-time.After(5 * time.Millisecond):
assert.NoError(t, offerDataChannel.Send([]byte{0xBE, 0xEF}))
}
}
})
answerPC.OnDataChannel(func(dataChannel *DataChannel) {
dataChannel.SetBufferedAmountLowThreshold(1)
var onBufferedAmountLowFired atomic.Bool
dataChannel.OnBufferedAmountLow(func() {
onBufferedAmountLowFired.Store(true)
<-gotAllMessages.Done()
})
var onMessageCount uint32
dataChannel.OnMessage(func(msg DataChannelMessage) {
if onBufferedAmountLowFired.Load() && atomic.AddUint32(&onMessageCount, 1) == 10 {
gotAllMessagesCancel()
}
})
})
<-gotAllMessages.Done()
closePairNow(t, offerPC, answerPC)
}
func TestOnBufferedAmountLowRespectsReadyState(t *testing.T) {
t.Run("fires when open", func(t *testing.T) {
dc := &DataChannel{}
dc.setReadyState(DataChannelStateOpen)
called := make(chan struct{}, 1)
dc.OnBufferedAmountLow(func() {
called <- struct{}{}
})
dc.mu.RLock()
handler := dc.onBufferedAmountLow
dc.mu.RUnlock()
handler()
select {
case <-called:
case <-time.After(time.Second):
assert.Fail(t, "expected OnBufferedAmountLow to fire when open")
}
})
t.Run("skips when not open", func(t *testing.T) {
dc := &DataChannel{}
dc.setReadyState(DataChannelStateClosing)
called := make(chan struct{}, 1)
dc.OnBufferedAmountLow(func() {
called <- struct{}{}
})
dc.mu.RLock()
handler := dc.onBufferedAmountLow
dc.mu.RUnlock()
handler()
select {
case <-called:
assert.Fail(t, "expected OnBufferedAmountLow to be ignored when not open")
case <-time.After(50 * time.Millisecond):
}
})
}
================================================
FILE: datachannel_js.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build js && wasm
// +build js,wasm
package webrtc
import (
"errors"
"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
onClosingHandler *js.Func
onMessageHandler *js.Func
onBufferedAmountLow *js.Func
onErrorHandler *js.Func
// A reference to the associated api object used by this datachannel
api *API
}
// JSValue returns the underlying RTCDataChannel
func (d *DataChannel) JSValue() js.Value {
return d.underlying
}
// 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) any {
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) any {
go f()
return js.Undefined()
})
d.onCloseHandler = &onCloseHandler
d.underlying.Set("onclose", onCloseHandler)
}
// FYI `OnClosing` is not implemented in the non-JS version of Pion.
func (d *DataChannel) OnClosing(f func()) {
if d.onClosingHandler != nil {
oldHandler := d.onClosingHandler
defer oldHandler.Release()
}
onClosingHandler := js.FuncOf(func(this js.Value, args []js.Value) any {
go f()
return js.Undefined()
})
d.onClosingHandler = &onClosingHandler
d.underlying.Set("onclosing", onClosingHandler)
}
func (d *DataChannel) OnError(f func(err error)) {
if d.onErrorHandler != nil {
oldHandler := d.onErrorHandler
defer oldHandler.Release()
}
onErrorHandler := js.FuncOf(func(this js.Value, args []js.Value) any {
event := args[0]
errorObj := event.Get("error")
// FYI RTCError has some extra properties, e.g. `errorDetail`:
// https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/error_event
errorMessage := errorObj.Get("message").String()
go f(errors.New(errorMessage))
return js.Undefined()
})
d.onErrorHandler = &onErrorHandler
d.underlying.Set("onerror", onErrorHandler)
}
// 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) any {
// 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 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) {
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.onClosingHandler != nil {
d.onClosingHandler.Release()
}
if d.onMessageHandler != nil {
d.onMessageHandler.Release()
}
if d.onBufferedAmountLow != nil {
d.onBufferedAmountLow.Release()
}
if d.onErrorHandler != nil {
d.onErrorHandler.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 ordered.IsUndefined() {
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 !d.underlying.Get("maxPacketLifeTime").IsUndefined() {
return valueToUint16Pointer(d.underlying.Get("maxPacketLifeTime"))
}
// 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 or equal to 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) any {
if d.ReadyState() != DataChannelStateOpen {
return js.Undefined()
}
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) any {
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,
}
}
================================================
FILE: datachannel_js_detach.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build js && wasm
// +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()
}
================================================
FILE: datachannel_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"fmt"
"io"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/pion/transport/v4/test"
"github.com/stretchr/testify/assert"
)
// expectedLabel represents the label of the data channel we are trying to test.
// Some other channels may have been created during initialization (in the Wasm
// bindings this is a requirement).
const expectedLabel = "data"
func closePairNow(tb testing.TB, pc1, pc2 io.Closer) {
tb.Helper()
var fail bool
if err := pc1.Close(); err != nil {
tb.Errorf("Failed to close PeerConnection: %v", err)
fail = true
}
if err := pc2.Close(); err != nil {
tb.Errorf("Failed to close PeerConnection: %v", err)
fail = true
}
if fail {
tb.FailNow()
}
}
func closePair(t *testing.T, pc1, pc2 io.Closer, done <-chan bool) {
t.Helper()
select {
case <-time.After(10 * time.Second):
assert.Fail(t, "closePair timed out waiting for done signal")
case <-done:
closePairNow(t, pc1, pc2)
}
}
func setUpDataChannelParametersTest(
t *testing.T,
options *DataChannelInit,
) (*PeerConnection, *PeerConnection, *DataChannel, chan bool) {
t.Helper()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
dc, err := offerPC.CreateDataChannel(expectedLabel, options)
assert.NoError(t, err)
return offerPC, answerPC, dc, done
}
func closeReliabilityParamTest(t *testing.T, pc1, pc2 *PeerConnection, done chan bool) {
t.Helper()
err := signalPair(pc1, pc2)
assert.NoError(t, err)
closePair(t, pc1, pc2, done)
}
func BenchmarkDataChannelSend2(b *testing.B) { benchmarkDataChannelSend(b, 2) }
func BenchmarkDataChannelSend4(b *testing.B) { benchmarkDataChannelSend(b, 4) }
func BenchmarkDataChannelSend8(b *testing.B) { benchmarkDataChannelSend(b, 8) }
func BenchmarkDataChannelSend16(b *testing.B) { benchmarkDataChannelSend(b, 16) }
func BenchmarkDataChannelSend32(b *testing.B) { benchmarkDataChannelSend(b, 32) }
// See https://github.com/pion/webrtc/issues/1516
func benchmarkDataChannelSend(b *testing.B, numChannels int) {
b.Helper()
offerPC, answerPC, err := newPair()
if err != nil {
b.Fatalf("Failed to create a PC pair for testing")
}
open := make(map[string]chan bool)
answerPC.OnDataChannel(func(d *DataChannel) {
if _, ok := open[d.Label()]; !ok {
// Ignore anything unknown channel label.
return
}
d.OnOpen(func() { open[d.Label()] <- true })
})
var wg sync.WaitGroup
for i := range numChannels {
label := fmt.Sprintf("dc-%d", i)
open[label] = make(chan bool)
wg.Add(1)
dc, err := offerPC.CreateDataChannel(label, nil)
assert.NoError(b, err)
dc.OnOpen(func() {
<-open[label]
for n := 0; n < b.N/numChannels; n++ {
if err := dc.SendText("Ping"); err != nil {
b.Fatalf("Unexpected error sending data (label=%q): %v", label, err)
}
}
wg.Done()
})
}
assert.NoError(b, signalPair(offerPC, answerPC))
wg.Wait()
closePairNow(b, offerPC, answerPC)
}
func TestDataChannel_Open(t *testing.T) {
const openOnceChannelCapacity = 2
t.Run("handler should be called once", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
openCalls := make(chan bool, openOnceChannelCapacity)
answerPC.OnDataChannel(func(d *DataChannel) {
if d.Label() != expectedLabel {
return
}
d.OnOpen(func() {
openCalls <- true
})
d.OnMessage(func(DataChannelMessage) {
go func() {
// Wait a little bit to ensure all messages are processed.
time.Sleep(100 * time.Millisecond)
done <- true
}()
})
})
dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
dc.OnOpen(func() {
assert.NoError(t, dc.SendText("Ping"), "Failed to send string on data channel")
})
assert.NoError(t, signalPair(offerPC, answerPC))
closePair(t, offerPC, answerPC, done)
assert.Len(t, openCalls, 1)
})
t.Run("handler should be called once when already negotiated", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
answerOpenCalls := make(chan bool, openOnceChannelCapacity)
offerOpenCalls := make(chan bool, openOnceChannelCapacity)
negotiated := true
ordered := true
dataChannelID := uint16(0)
answerDC, err := answerPC.CreateDataChannel(expectedLabel, &DataChannelInit{
ID: &dataChannelID,
Negotiated: &negotiated,
Ordered: &ordered,
})
assert.NoError(t, err)
offerDC, err := offerPC.CreateDataChannel(expectedLabel, &DataChannelInit{
ID: &dataChannelID,
Negotiated: &negotiated,
Ordered: &ordered,
})
assert.NoError(t, err)
answerDC.OnMessage(func(DataChannelMessage) {
go func() {
// Wait a little bit to ensure all messages are processed.
time.Sleep(100 * time.Millisecond)
done <- true
}()
})
answerDC.OnOpen(func() {
answerOpenCalls <- true
})
offerDC.OnOpen(func() {
offerOpenCalls <- true
assert.NoError(t, offerDC.SendText("Ping"), "Failed to send string on data channel")
})
assert.NoError(t, signalPair(offerPC, answerPC))
closePair(t, offerPC, answerPC, done)
assert.Len(t, answerOpenCalls, 1)
assert.Len(t, offerOpenCalls, 1)
})
}
func TestDataChannel_Send(t *testing.T) { //nolint:cyclop
t.Run("before signaling", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
answerPC.OnDataChannel(func(d *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if d.Label() != expectedLabel {
return
}
d.OnMessage(func(DataChannelMessage) {
assert.NoError(t, d.Send([]byte("Pong")), "Failed to send string on data channel")
})
assert.True(t, d.Ordered(), "Ordered should be set to true")
})
dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
assert.True(t, dc.Ordered(), "Ordered should be set to true")
dc.OnOpen(func() {
assert.NoError(t, dc.SendText("Ping"), "Failed to send string on data channel")
})
dc.OnMessage(func(DataChannelMessage) {
done <- true
})
err = signalPair(offerPC, answerPC)
assert.NoError(t, err)
closePair(t, offerPC, answerPC, done)
})
t.Run("after connected", func(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
done := make(chan bool)
answerPC.OnDataChannel(func(d *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if d.Label() != expectedLabel {
return
}
d.OnMessage(func(DataChannelMessage) {
assert.NoError(t, d.Send([]byte("Pong")), "Failed to send string on data channel")
})
assert.True(t, d.Ordered(), "Ordered should be set to true")
})
once := &sync.Once{}
offerPC.OnICEConnectionStateChange(func(state ICEConnectionState) {
if state == ICEConnectionStateConnected || state == ICEConnectionStateCompleted {
// wasm fires completed state multiple times
once.Do(func() {
dc, createErr := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, createErr)
assert.True(t, dc.Ordered(), "Ordered should be set to true")
dc.OnMessage(func(DataChannelMessage) {
done <- true
})
if e := dc.SendText("Ping"); e != nil {
// wasm binding doesn't fire OnOpen (we probably already missed it)
dc.OnOpen(func() {
assert.NoError(t, dc.SendText("Ping"), "Failed to send string on data channel")
})
}
})
}
})
err = signalPair(offerPC, answerPC)
assert.NoError(t, err)
closePair(t, offerPC, answerPC, done)
})
}
func TestDataChannel_Close(t *testing.T) {
report := test.CheckRoutines(t)
defer report()
t.Run("Close after PeerConnection Closed", func(t *testing.T) {
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
closePairNow(t, offerPC, answerPC)
assert.NoError(t, dc.Close())
})
t.Run("Close before connected", func(t *testing.T) {
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
dc, err := offerPC.CreateDataChannel(expectedLabel, nil)
assert.NoError(t, err)
assert.NoError(t, dc.Close())
closePairNow(t, offerPC, answerPC)
})
}
func TestDataChannelParameters(t *testing.T) { //nolint:cyclop
report := test.CheckRoutines(t)
defer report()
t.Run("MaxPacketLifeTime exchange", func(t *testing.T) {
ordered := true
maxPacketLifeTime := uint16(3)
options := &DataChannelInit{
Ordered: &ordered,
MaxPacketLifeTime: &maxPacketLifeTime,
}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
// Check if parameters are correctly set
assert.Equal(t, dc.Ordered(), ordered, "Ordered should be same value as set in DataChannelInit")
if assert.NotNil(t, dc.MaxPacketLifeTime(), "should not be nil") {
assert.Equal(t, maxPacketLifeTime, *dc.MaxPacketLifeTime(), "should match")
}
answerPC.OnDataChannel(func(d *DataChannel) {
if d.Label() != expectedLabel {
return
}
// Check if parameters are correctly set
assert.Equal(t, d.Ordered(), ordered, "Ordered should be same value as set in DataChannelInit")
if assert.NotNil(t, d.MaxPacketLifeTime(), "should not be nil") {
assert.Equal(t, maxPacketLifeTime, *d.MaxPacketLifeTime(), "should match")
}
done <- true
})
closeReliabilityParamTest(t, offerPC, answerPC, done)
})
t.Run("MaxRetransmits exchange", func(t *testing.T) {
ordered := false
maxRetransmits := uint16(3000)
options := &DataChannelInit{
Ordered: &ordered,
MaxRetransmits: &maxRetransmits,
}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
// Check if parameters are correctly set
assert.False(t, dc.Ordered(), "Ordered should be set to false")
if assert.NotNil(t, dc.MaxRetransmits(), "should not be nil") {
assert.Equal(t, maxRetransmits, *dc.MaxRetransmits(), "should match")
}
answerPC.OnDataChannel(func(d *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if d.Label() != expectedLabel {
return
}
// Check if parameters are correctly set
assert.False(t, d.Ordered(), "Ordered should be set to false")
if assert.NotNil(t, d.MaxRetransmits(), "should not be nil") {
assert.Equal(t, maxRetransmits, *d.MaxRetransmits(), "should match")
}
done <- true
})
closeReliabilityParamTest(t, offerPC, answerPC, done)
})
t.Run("Protocol exchange", func(t *testing.T) {
protocol := "json"
options := &DataChannelInit{
Protocol: &protocol,
}
offerPC, answerPC, dc, done := setUpDataChannelParametersTest(t, options)
// Check if parameters are correctly set
assert.Equal(t, protocol, dc.Protocol(), "Protocol should match DataChannelInit")
answerPC.OnDataChannel(func(d *DataChannel) {
// Make sure this is the data channel we were looking for. (Not the one
// created in signalPair).
if d.Label() != expectedLabel {
return
}
// Check if parameters are correctly set
assert.Equal(t, protocol, d.Protocol(), "Protocol should match what channel creator declared")
done <- true
})
closeReliabilityParamTest(t, offerPC, answerPC, done)
})
t.Run("Negotiated exchange", func(t *testing.T) {
const expectedMessage = "Hello World"
negotiated := true
var id uint16 = 500
options := &DataChannelInit{
Negotiated: &negotiated,
ID: &id,
}
offerPC, answerPC, offerDatachannel, done := setUpDataChannelParametersTest(t, options)
answerDatachannel, err := answerPC.CreateDataChannel(expectedLabel, options)
assert.NoError(t, err)
answerPC.OnDataChannel(func(d *DataChannel) {
// Ignore our default channel, exists to force ICE candidates. See signalPair for more info
assert.Equal(t, "initial_data_channel", d.Label(), "OnDataChannel must not be fired when negotiated == true")
})
offerPC.OnDataChannel(func(*DataChannel) {
assert.Fail(t, "OnDataChannel must not be fired when negotiated == true")
})
seenAnswerMessage := &atomic.Bool{}
seenOfferMessage := &atomic.Bool{}
answerDatachannel.OnMessage(func(msg DataChannelMessage) {
if msg.IsString && string(msg.Data) == expectedMessage {
seenAnswerMessage.Store(true)
}
})
offerDatachannel.OnMessage(func(msg DataChannelMessage) {
if msg.IsString && string(msg.Data) == expectedMessage {
seenOfferMessage.Store(true)
}
})
go func() {
for seenAnswerMessage.Load() && seenOfferMessage.Load() {
if offerDatachannel.ReadyState() == DataChannelStateOpen {
assert.NoError(t, offerDatachannel.SendText(expectedMessage))
}
if answerDatachannel.ReadyState() == DataChannelStateOpen {
assert.NoError(t, answerDatachannel.SendText(expectedMessage))
}
time.Sleep(500 * time.Millisecond)
}
done <- true
}()
closeReliabilityParamTest(t, offerPC, answerPC, done)
})
}
================================================
FILE: datachannelinit.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}
================================================
FILE: datachannelmessage.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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
}
================================================
FILE: datachannelparameters.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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"`
}
================================================
FILE: datachannelstate.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
// DataChannelState indicates the state of a data channel.
type DataChannelState int
const (
// DataChannelStateUnknown is the enum's zero-value.
DataChannelStateUnknown DataChannelState = iota
// 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
// 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 DataChannelStateUnknown
}
}
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()
}
}
// MarshalText implements encoding.TextMarshaler.
func (t DataChannelState) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (t *DataChannelState) UnmarshalText(b []byte) error {
*t = newDataChannelState(string(b))
return nil
}
================================================
FILE: datachannelstate_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewDataChannelState(t *testing.T) {
testCases := []struct {
stateString string
expectedState DataChannelState
}{
{ErrUnknownType.Error(), DataChannelStateUnknown},
{"connecting", DataChannelStateConnecting},
{"open", DataChannelStateOpen},
{"closing", DataChannelStateClosing},
{"closed", DataChannelStateClosed},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedState,
newDataChannelState(testCase.stateString),
"testCase: %d %v", i, testCase,
)
}
}
func TestDataChannelState_String(t *testing.T) {
testCases := []struct {
state DataChannelState
expectedString string
}{
{DataChannelStateUnknown, ErrUnknownType.Error()},
{DataChannelStateConnecting, "connecting"},
{DataChannelStateOpen, "open"},
{DataChannelStateClosing, "closing"},
{DataChannelStateClosed, "closed"},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedString,
testCase.state.String(),
"testCase: %d %v", i, testCase,
)
}
}
================================================
FILE: dtlsfingerprint.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
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 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"`
}
================================================
FILE: dtlsparameters.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
// DTLSParameters holds information relating to DTLS configuration.
type DTLSParameters struct {
Role DTLSRole `json:"role"`
Fingerprints []DTLSFingerprint `json:"fingerprints"`
}
================================================
FILE: dtlsrole.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"github.com/pion/sdp/v3"
)
// DTLSRole indicates the role of the DTLS transport.
type DTLSRole byte
const (
// DTLSRoleUnknown is the enum's zero-value.
DTLSRoleUnknown DTLSRole = iota
// 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
// 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 ErrUnknownType.Error()
}
}
// Extract the dtls role from a session description. The decision is made from
// the first role we we parse. If no role can be found we return DTLSRoleAuto.
func dtlsRoleFromSDP(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)
}
}
================================================
FILE: dtlsrole_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"fmt"
"testing"
"github.com/pion/sdp/v3"
"github.com/stretchr/testify/assert"
)
func TestDTLSRole_String(t *testing.T) {
testCases := []struct {
role DTLSRole
expectedString string
}{
{DTLSRoleUnknown, ErrUnknownType.Error()},
{DTLSRoleAuto, "auto"},
{DTLSRoleClient, "client"},
{DTLSRoleServer, "server"},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedString,
testCase.role.String(),
"testCase: %d %v", i, testCase,
)
}
}
func TestDTLSRoleFromSDP(t *testing.T) {
parseSDP := func(raw string) *sdp.SessionDescription {
parsed := &sdp.SessionDescription{}
assert.NoError(t, parsed.Unmarshal([]byte(raw)))
return parsed
}
const noMedia = `v=0
o=- 4596489990601351948 2 IN IP4 127.0.0.1
s=-
t=0 0
`
const mediaNoSetup = `v=0
o=- 4596489990601351948 2 IN IP4 127.0.0.1
s=-
t=0 0
m=application 47299 DTLS/SCTP 5000
c=IN IP4 192.168.20.129
`
const mediaSetupDeclared = `v=0
o=- 4596489990601351948 2 IN IP4 127.0.0.1
s=-
t=0 0
m=application 47299 DTLS/SCTP 5000
c=IN IP4 192.168.20.129
a=setup:%s
`
testCases := []struct {
test string
sessionDescription *sdp.SessionDescription
expectedRole DTLSRole
}{
{"nil SessionDescription", nil, DTLSRoleAuto},
{"No MediaDescriptions", parseSDP(noMedia), DTLSRoleAuto},
{"MediaDescription, no setup", parseSDP(mediaNoSetup), DTLSRoleAuto},
{"MediaDescription, setup:actpass", parseSDP(fmt.Sprintf(mediaSetupDeclared, "actpass")), DTLSRoleAuto},
{"MediaDescription, setup:passive", parseSDP(fmt.Sprintf(mediaSetupDeclared, "passive")), DTLSRoleServer},
{"MediaDescription, setup:active", parseSDP(fmt.Sprintf(mediaSetupDeclared, "active")), DTLSRoleClient},
}
for _, testCase := range testCases {
assert.Equal(t,
testCase.expectedRole,
dtlsRoleFromSDP(testCase.sessionDescription),
"TestDTLSRoleFromSDP (%s)", testCase.test,
)
}
}
================================================
FILE: dtlstransport.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go: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/v3"
"github.com/pion/dtls/v3/pkg/crypto/fingerprint"
"github.com/pion/interceptor"
"github.com/pion/logging"
"github.com/pion/rtcp"
"github.com/pion/srtp/v3"
"github.com/pion/webrtc/v4/internal/mux"
"github.com/pion/webrtc/v4/internal/util"
"github.com/pion/webrtc/v4/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)
internalOnCloseHandler func()
conn *dtls.Conn
srtpSession, srtcpSession atomic.Value
srtpEndpoint, srtcpEndpoint *mux.Endpoint
simulcastStreams []simulcastStreamPair
srtpReady chan struct{}
dtlsMatcher mux.MatchFunc
api *API
log logging.LeveledLogger
}
type simulcastStreamPair struct {
srtp *srtp.ReadStreamSRTP
srtcp *srtp.ReadStreamSRTCP
}
type streamsForSSRCResult struct {
rtpReadStream *srtp.ReadStreamSRTP
rtpInterceptor interceptor.RTPReader
rtcpReadStream *srtp.ReadStreamSRTCP
rtcpInterceptor interceptor.RTCPReader
}
// 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) {
trans := &DTLSTransport{
iceTransport: transport,
api: api,
state: DTLSTransportStateNew,
dtlsMatcher: mux.MatchDTLS,
srtpReady: make(chan struct{}),
log: api.settingEngine.LoggerFactory.NewLogger("DTLSTransport"),
}
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}
}
trans.certificates = append(trans.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
}
trans.certificates = []Certificate{*certificate}
}
return trans, 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
}
// WriteRTCP sends a user provided RTCP packet to the connected peer. If no peer is connected the
// packet is discarded.
func (t *DTLSTransport) WriteRTCP(pkts []rtcp.Packet) (int, error) {
raw, err := rtcp.Marshal(pkts)
if err != nil {
return 0, err
}
srtcpSession, err := t.getSRTCPSession()
if err != nil {
return 0, err
}
writeStream, err := srtcpSession.OpenWriteStream()
if err != nil {
// nolint
return 0, fmt.Errorf("%w: %v", errPeerConnWriteRTCPOpenWriteStream, err)
}
return writeStream.Write(raw)
}
// 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, ok := t.conn.ConnectionState()
if !ok {
// nolint
return fmt.Errorf("%w: Failed to get DTLS ConnectionState", errDtlsKeyExtractionFailed)
}
err := srtpConfig.ExtractSessionKeysFromDTLS(&connState, t.role() == DTLSRoleClient)
if err != nil {
// nolint
return fmt.Errorf("%w: %v", errDtlsKeyExtractionFailed, err)
}
srtpSession, err := srtp.NewSessionSRTP(t.srtpEndpoint, srtpConfig)
if err != nil {
// nolint
return fmt.Errorf("%w: %v", errFailedToStartSRTP, err)
}
srtcpSession, err := srtp.NewSessionSRTCP(t.srtcpEndpoint, srtpConfig)
if err != nil {
// nolint
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, ok := t.srtpSession.Load().(*srtp.SessionSRTP); ok {
return value, nil
}
return nil, errDtlsTransportNotStarted
}
func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) {
if value, ok := t.srtcpSession.Load().(*srtp.SessionSRTCP); ok {
return value, 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 {
role, certificate, err := t.prepareStart(remoteParameters)
if err != nil {
return err
}
dtlsEndpoint := t.iceTransport.newEndpoint(mux.MatchDTLS)
dtlsEndpoint.SetOnClose(t.internalOnCloseHandler)
sharedOpts := t.dtlsSharedOptions(certificate)
dtlsConn, err := t.connectDTLS(dtlsEndpoint, role, sharedOpts)
if err != nil {
dtlsEndpoint.SetOnClose(nil)
_ = dtlsEndpoint.Close()
return t.failStart(err)
}
if err = t.handshakeDTLS(dtlsConn); err != nil {
dtlsEndpoint.SetOnClose(nil)
_ = dtlsConn.Close()
return t.failStart(err)
}
if err = t.completeStart(dtlsConn); err != nil {
dtlsEndpoint.SetOnClose(nil)
_ = dtlsConn.Close()
return err
}
return nil
}
func (t *DTLSTransport) prepareStart(remoteParameters DTLSParameters) (DTLSRole, tls.Certificate, error) {
t.lock.Lock()
defer t.lock.Unlock()
if err := t.ensureICEConn(); err != nil {
return DTLSRole(0), tls.Certificate{}, err
}
if t.state != DTLSTransportStateNew {
return DTLSRole(0), tls.Certificate{}, &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(), tls.Certificate{
Certificate: [][]byte{cert.x509Cert.Raw},
PrivateKey: cert.privateKey,
}, nil
}
func (t *DTLSTransport) dtlsSharedOptions(certificate tls.Certificate) []dtls.Option {
sharedOpts := []dtls.Option{
dtls.WithCertificates(certificate),
dtls.WithSRTPProtectionProfiles(t.srtpProtectionProfiles()...),
dtls.WithExtendedMasterSecret(t.api.settingEngine.dtls.extendedMasterSecret),
dtls.WithInsecureSkipVerify(!t.api.settingEngine.dtls.disableInsecureSkipVerify),
dtls.WithLoggerFactory(t.api.settingEngine.LoggerFactory),
dtls.WithVerifyPeerCertificate(t.verifyPeerCertificateFunc()),
}
if t.api.settingEngine.dtls.customCipherSuites != nil {
sharedOpts = append(
sharedOpts,
dtls.WithCustomCipherSuites(t.api.settingEngine.dtls.customCipherSuites),
)
}
if t.api.settingEngine.dtls.retransmissionInterval > 0 {
sharedOpts = append(
sharedOpts,
dtls.WithFlightInterval(t.api.settingEngine.dtls.retransmissionInterval),
)
}
if t.api.settingEngine.replayProtection.DTLS != nil {
sharedOpts = append(
sharedOpts,
dtls.WithReplayProtectionWindow(int(*t.api.settingEngine.replayProtection.DTLS)), //nolint:gosec // G115
)
}
if t.api.settingEngine.dtls.cipherSuites != nil {
sharedOpts = append(
sharedOpts,
dtls.WithCipherSuites(t.api.settingEngine.dtls.cipherSuites...),
)
}
if len(t.api.settingEngine.dtls.ellipticCurves) > 0 {
sharedOpts = append(
sharedOpts,
dtls.WithEllipticCurves(t.api.settingEngine.dtls.ellipticCurves...),
)
}
if t.api.settingEngine.dtls.rootCAs != nil {
sharedOpts = append(sharedOpts, dtls.WithRootCAs(t.api.settingEngine.dtls.rootCAs))
}
if t.api.settingEngine.dtls.keyLogWriter != nil {
sharedOpts = append(sharedOpts, dtls.WithKeyLogWriter(t.api.settingEngine.dtls.keyLogWriter))
}
if len(t.api.settingEngine.dtls.supportedProtocols) > 0 {
sharedOpts = append(
sharedOpts,
dtls.WithSupportedProtocols(t.api.settingEngine.dtls.supportedProtocols...),
)
}
return sharedOpts
}
func (t *DTLSTransport) srtpProtectionProfiles() []dtls.SRTPProtectionProfile {
if len(t.api.settingEngine.srtpProtectionProfiles) > 0 {
return t.api.settingEngine.srtpProtectionProfiles
}
return defaultSrtpProtectionProfiles()
}
func (t *DTLSTransport) verifyPeerCertificateFunc() func([][]byte, [][]*x509.Certificate) error {
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return errNoRemoteCertificate
}
t.lock.Lock()
defer t.lock.Unlock()
t.remoteCertificate = rawCerts[0]
if t.api.settingEngine.disableCertificateFingerprintVerification {
return nil
}
parsedRemoteCert, err := x509.ParseCertificate(t.remoteCertificate)
if err != nil {
return err
}
return t.validateFingerPrint(parsedRemoteCert)
}
}
func (t *DTLSTransport) connectDTLS(
dtlsEndpoint *mux.Endpoint,
role DTLSRole,
sharedOpts []dtls.Option,
) (*dtls.Conn, error) {
if role == DTLSRoleClient {
clientOpts := t.toDTLSClientOptions(sharedOpts)
return dtls.ClientWithOptions(
dtlsEndpoint,
dtlsEndpoint.RemoteAddr(),
clientOpts...,
)
}
serverOpts := t.toDTLSServerOptions(sharedOpts)
return dtls.ServerWithOptions(
dtlsEndpoint,
dtlsEndpoint.RemoteAddr(),
serverOpts...,
)
}
func (t *DTLSTransport) toDTLSServerOptions(sharedOpts []dtls.Option) []dtls.ServerOption {
serverOpts := make([]dtls.ServerOption, 0, len(sharedOpts)+5)
for _, opt := range sharedOpts {
serverOpts = append(serverOpts, opt)
}
clientAuth := dtls.RequireAnyClientCert
if t.api.settingEngine.dtls.clientAuth != nil {
clientAuth = *t.api.settingEngine.dtls.clientAuth
}
serverOpts = append(serverOpts,
dtls.WithClientAuth(clientAuth),
dtls.WithClientCAs(t.api.settingEngine.dtls.clientCAs),
dtls.WithInsecureSkipVerifyHello(t.api.settingEngine.dtls.insecureSkipHelloVerify),
)
if t.api.settingEngine.dtls.serverHelloMessageHook != nil {
serverOpts = append(
serverOpts,
dtls.WithServerHelloMessageHook(t.api.settingEngine.dtls.serverHelloMessageHook),
)
}
if t.api.settingEngine.dtls.certificateRequestMessageHook != nil {
serverOpts = append(
serverOpts,
dtls.WithCertificateRequestMessageHook(t.api.settingEngine.dtls.certificateRequestMessageHook),
)
}
return serverOpts
}
func (t *DTLSTransport) toDTLSClientOptions(sharedOpts []dtls.Option) []dtls.ClientOption {
clientOpts := make([]dtls.ClientOption, 0, len(sharedOpts)+1)
for _, opt := range sharedOpts {
clientOpts = append(clientOpts, opt)
}
if t.api.settingEngine.dtls.clientHelloMessageHook != nil {
clientOpts = append(
clientOpts,
dtls.WithClientHelloMessageHook(t.api.settingEngine.dtls.clientHelloMessageHook),
)
}
return clientOpts
}
func (t *DTLSTransport) handshakeDTLS(dtlsConn *dtls.Conn) error {
if t.api.settingEngine.dtls.connectContextMaker == nil {
return dtlsConn.Handshake()
}
handshakeCtx, cancel := t.api.settingEngine.dtls.connectContextMaker()
if cancel != nil {
defer cancel()
}
return dtlsConn.HandshakeContext(handshakeCtx)
}
func (t *DTLSTransport) completeStart(dtlsConn *dtls.Conn) error {
srtpProtectionProfile, err := srtpProtectionProfileFromDTLSConn(dtlsConn)
t.lock.Lock()
defer t.lock.Unlock()
if err != nil {
t.onStateChange(DTLSTransportStateFailed)
return err
}
t.srtpProtectionProfile = srtpProtectionProfile
t.conn = dtlsConn
t.onStateChange(DTLSTransportStateConnected)
return t.startSRTP()
}
func (t *DTLSTransport) failStart(err error) error {
t.lock.Lock()
defer t.lock.Unlock()
t.onStateChange(DTLSTransportStateFailed)
return err
}
func srtpProtectionProfileFromDTLSConn(dtlsConn *dtls.Conn) (srtp.ProtectionProfile, error) {
srtpProfile, ok := dtlsConn.SelectedSRTPProtectionProfile()
if !ok {
return 0, ErrNoSRTPProtectionProfile
}
return srtpProtectionProfileFromDTLS(srtpProfile)
}
func srtpProtectionProfileFromDTLS(srtpProfile dtls.SRTPProtectionProfile) (srtp.ProtectionProfile, error) {
switch srtpProfile {
case dtls.SRTP_AEAD_AES_128_GCM:
return srtp.ProtectionProfileAeadAes128Gcm, nil
case dtls.SRTP_AEAD_AES_256_GCM:
return srtp.ProtectionProfileAeadAes256Gcm, nil
case dtls.SRTP_AES128_CM_HMAC_SHA1_80:
return srtp.ProtectionProfileAes128CmHmacSha1_80, nil
case dtls.SRTP_NULL_HMAC_SHA1_80:
return srtp.ProtectionProfileNullHmacSha1_80, nil
default:
return 0, ErrNoSRTPProtectionProfile
}
}
// 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 srtpSession, err := t.getSRTPSession(); err == nil && srtpSession != nil {
closeErrs = append(closeErrs, srtpSession.Close())
}
if srtcpSession, err := t.getSRTCPSession(); err == nil && srtcpSession != nil {
closeErrs = append(closeErrs, srtcpSession.Close())
}
for i := range t.simulcastStreams {
closeErrs = append(closeErrs, t.simulcastStreams[i].srtp.Close())
closeErrs = append(closeErrs, t.simulcastStreams[i].srtcp.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 {
return errICEConnectionNotStarted
}
return nil
}
func (t *DTLSTransport) storeSimulcastStream(
srtpReadStream *srtp.ReadStreamSRTP,
srtcpReadStream *srtp.ReadStreamSRTCP,
) {
t.lock.Lock()
defer t.lock.Unlock()
t.simulcastStreams = append(t.simulcastStreams, simulcastStreamPair{srtpReadStream, srtcpReadStream})
}
func (t *DTLSTransport) streamsForSSRC(
ssrc SSRC,
streamInfo interceptor.StreamInfo,
) (*streamsForSSRCResult, error) {
srtpSession, err := t.getSRTPSession()
if err != nil {
return nil, err
}
rtpReadStream, err := srtpSession.OpenReadStream(uint32(ssrc))
if err != nil {
return nil, err
}
rtpInterceptor := t.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 := t.getSRTCPSession()
if err != nil {
return nil, err
}
rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(ssrc))
if err != nil {
return nil, err
}
rtcpInterceptor := t.api.interceptor.BindRTCPReader(interceptor.RTCPReaderFunc(
func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
n, err = rtcpReadStream.Read(in)
return n, a, err
}),
)
return &streamsForSSRCResult{
rtpReadStream: rtpReadStream,
rtpInterceptor: rtpInterceptor,
rtcpReadStream: rtcpReadStream,
rtcpInterceptor: rtcpInterceptor,
}, nil
}
================================================
FILE: dtlstransport_js.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build js && wasm
// +build js,wasm
package webrtc
import "syscall/js"
// 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 {
// Pointer to the underlying JavaScript DTLSTransport object.
underlying js.Value
}
// JSValue returns the underlying RTCDtlsTransport
func (r *DTLSTransport) JSValue() js.Value {
return r.underlying
}
// ICETransport returns the currently-configured *ICETransport or nil
// if one has not been configured
func (r *DTLSTransport) ICETransport() *ICETransport {
underlying := r.underlying.Get("iceTransport")
if underlying.IsNull() || underlying.IsUndefined() {
return nil
}
return &ICETransport{
underlying: underlying,
}
}
func (t *DTLSTransport) GetRemoteCertificate() []byte {
if t.underlying.IsNull() || t.underlying.IsUndefined() {
return nil
}
// Firefox does not support getRemoteCertificates: https://bugzilla.mozilla.org/show_bug.cgi?id=1805446
jsGet := t.underlying.Get("getRemoteCertificates")
if jsGet.IsUndefined() || jsGet.IsNull() {
return nil
}
jsCerts := t.underlying.Call("getRemoteCertificates")
if jsCerts.Length() == 0 {
return nil
}
buf := jsCerts.Index(0)
u8 := js.Global().Get("Uint8Array").New(buf)
if u8.Length() == 0 {
return nil
}
cert := make([]byte, u8.Length())
js.CopyBytesToGo(cert, u8)
return cert
}
================================================
FILE: dtlstransport_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
//go:build !js
package webrtc
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"errors"
"io"
"net"
"reflect"
"regexp"
"testing"
"time"
"github.com/pion/dtls/v3"
dtlsElliptic "github.com/pion/dtls/v3/pkg/crypto/elliptic"
"github.com/pion/dtls/v3/pkg/protocol/handshake"
"github.com/pion/srtp/v3"
"github.com/pion/transport/v4/test"
"github.com/pion/webrtc/v4/internal/mux"
"github.com/stretchr/testify/assert"
)
// An invalid fingerprint MUST cause DTLSTransport to go to failed state.
func TestInvalidFingerprintCausesFailed(t *testing.T) { //nolint:cyclop
lim := test.TimeOut(time.Second * 10)
defer lim.Stop()
report := test.CheckRoutines(t)
defer report()
pcOffer, err := NewPeerConnection(Configuration{})
assert.NoError(t, err)
pcAnswer, err := NewPeerConnection(Configuration{})
assert.NoError(t, err)
pcAnswer.OnDataChannel(func(_ *DataChannel) {
assert.Fail(t, "A DataChannel must not be created when Fingerprint verification fails")
})
defer closePairNow(t, pcOffer, pcAnswer)
// Set up DTLS state tracking BEFORE starting the connection process
// to avoid missing the state transition
offerDTLSFailed := make(chan struct{})
answerDTLSFailed := make(chan struct{})
pcOffer.SCTP().Transport().OnStateChange(func(state DTLSTransportState) {
if state == DTLSTransportStateFailed {
select {
case <-offerDTLSFailed:
// Already closed
default:
close(offerDTLSFailed)
}
}
})
pcAnswer.SCTP().Transport().OnStateChange(func(state DTLSTransportState) {
if state == DTLSTransportStateFailed {
select {
case <-answerDTLSFailed:
// Already closed
default:
close(answerDTLSFailed)
}
}
})
offerChan := make(chan SessionDescription)
pcOffer.OnICECandidate(func(candidate *ICECandidate) {
if candidate == nil {
offerChan <- *pcOffer.PendingLocalDescription()
}
})
// Also wait for PeerConnection to close (may take longer due to cleanup)
offerConnectionHasClosed := untilConnectionState(PeerConnectionStateClosed, pcOffer)
answerConnectionHasClosed := untilConnectionState(PeerConnectionStateClosed, pcAnswer)
_, err = pcOffer.CreateDataChannel("unusedDataChannel", nil)
assert.NoError(t, err)
offer, err := pcOffer.CreateOffer(nil)
assert.NoError(t, err)
assert.NoError(t, pcOffer.SetLocalDescription(offer))
select {
case offer := <-offerChan:
// Replace with invalid fingerprint
re := regexp.MustCompile(`sha-256 (.*?)\r`)
offer.SDP = re.ReplaceAllString(
offer.SDP,
"sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r",
)
assert.NoError(t, pcAnswer.SetRemoteDescription(offer))
answer, err := pcAnswer.CreateAnswer(nil)
assert.NoError(t, err)
assert.NoError(t, pcAnswer.SetLocalDescription(answer))
answer.SDP = re.ReplaceAllString(
answer.SDP,
"sha-256 AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA\r",
)
assert.NoError(t, pcOffer.SetRemoteDescription(answer))
case <-time.After(5 * time.Second):
assert.Fail(t, "timed out waiting to receive offer")
}
// Wait for DTLS to fail (should happen quickly after ICE connects, ~1-2 seconds normally,
// but may take longer with race detector due to ICE connectivity checks)
select {
case <-offerDTLSFailed:
// Expected - offer DTLS failed due to invalid fingerprint
case <-time.After(7 * time.Second):
assert.Fail(t, "timed out waiting for offer DTLS to fail")
}
select {
case <-answerDTLSFailed:
// Expected - answer DTLS failed due to invalid fingerprint
case <-time.After(7 * time.Second):
assert.Fail(t, "timed out waiting for answer DTLS to fail")
}
// Wait for PeerConnection to close (may take longer due to cleanup)
offerConnectionHasClosed.Wait()
answerConnectionHasClosed.Wait()
assert.Contains(
t, []DTLSTransportState{DTLSTransportStateClosed, DTLSTransportStateFailed}, pcOffer.SCTP().Transport().State(),
"DTLS Transport should be closed or failed",
)
assert.Nil(t, pcOffer.SCTP().Transport().conn)
assert.Contains(
t, []DTLSTransportState{DTLSTransportStateClosed, DTLSTransportStateFailed}, pcAnswer.SCTP().Transport().State(),
"DTLS Transport should be closed or failed",
)
assert.Nil(t, pcAnswer.SCTP().Transport().conn)
}
func TestPeerConnection_DTLSRoleSettingEngine(t *testing.T) {
runTest := func(r DTLSRole) {
s := SettingEngine{}
assert.NoError(t, s.SetAnsweringDTLSRole(r))
offerPC, err := NewAPI(WithSettingEngine(s)).NewPeerConnection(Configuration{})
assert.NoError(t, err)
answerPC, err := NewAPI(WithSettingEngine(s)).NewPeerConnection(Configuration{})
assert.NoError(t, err)
assert.NoError(t, signalPair(offerPC, answerPC))
connectionComplete := untilConnectionState(PeerConnectionStateConnected, answerPC)
connectionComplete.Wait()
closePairNow(t, offerPC, answerPC)
}
report := test.CheckRoutines(t)
defer report()
t.Run("Server", func(*testing.T) {
runTest(DTLSRoleServer)
})
t.Run("Client", func(*testing.T) {
runTest(DTLSRoleClient)
})
}
type errConn struct {
localAddr net.Addr
remoteAddr net.Addr
readErr error
writeErr error
}
func (c *errConn) Read([]byte) (int, error) { return 0, c.readErr }
func (c *errConn) Write([]byte) (int, error) { return 0, c.writeErr }
func (c *errConn) Close() error { return nil }
func (c *errConn) LocalAddr() net.Addr { return c.localAddr }
func (c *errConn) RemoteAddr() net.Addr { return c.remoteAddr }
func (c *errConn) SetDeadline(time.Time) error { return nil }
func (c *errConn) SetReadDeadline(time.Time) error { return nil }
func (c *errConn) SetWriteDeadline(time.Time) error { return nil }
type failingPacketConn struct {
localAddr net.Addr
readErr error
writeErr error
}
var errTestWriteFailed = errors.New("write failed")
func (c *failingPacketConn) ReadFrom([]byte) (int, net.Addr, error) {
return 0, c.localAddr, c.readErr
}
func (c *failingPacketConn) WriteTo([]byte, net.Addr) (int, error) {
return 0, c.writeErr
}
func (c *failingPacketConn) Close() error { return nil }
func (c *failingPacketConn) LocalAddr() net.Addr { return c.localAddr }
func (c *failingPacketConn) SetDeadline(time.Time) error { return nil }
func (c *failingPacketConn) SetReadDeadline(time.Time) error { return nil }
func (c *failingPacketConn) SetWriteDeadline(time.Time) error { return nil }
func TestDTLSTransport_Start_ErrICEConnectionNotStarted(t *testing.T) {
transport := &DTLSTransport{state: DTLSTransportStateNew}
err := transport.Start(DTLSParameters{Role: DTLSRoleServer})
assert.ErrorIs(t, err, errICEConnectionNotStarted)
assert.Equal(t, DTLSTransportStateNew, transport.State())
}
func TestDTLSTransport_Start_ConnectErrorFailsTransport(t *testing.T) {
lim := test.TimeOut(time.Second)
defer lim.Stop()
report := test.CheckRoutines(t)
defer report()
api := NewAPI()
loggerFactory := api.settingEngine.LoggerFactory
localConn, remoteConn := net.Pipe()
defer func() { _ = remoteConn.Close() }()
iceTransport := NewICETransport(nil, loggerFactory)
iceTransport.mux = mux.NewMux(mux.Config{
Conn: localConn,
BufferSize: 1500,
LoggerFactory: loggerFactory,
})
defer func() { _ = iceTransport.mux.Close() }()
transport, err := api.NewDTLSTransport(iceTransport, nil)
assert.NoError(t, err)
assert.Equal(t, DTLSTransportStateNew, transport.State())
transport.api.settingEngine.dtls.cipherSuites = []dtls.CipherSuiteID{}
err = transport.Start(DTLSParameters{Role: DTLSRoleServer})
assert.Error(t, err)
assert.Equal(t, DTLSTransportStateFailed, transport.State())
assert.Nil(t, transport.conn)
assert.Equal(t, 2, reflect.ValueOf(iceTransport.mux).Elem().FieldByName("endpoints").Len())
}
func TestDTLSTransport_Start_HandshakeErrorFailsTransport(t *testing.T) {
lim := test.TimeOut(time.Second)
defer lim.Stop()
report := test.CheckRoutines(t)
defer report()
api := NewAPI()
loggerFactory := api.settingEngine.LoggerFactory
conn := &errConn{
localAddr: &net.UDPAddr{IP: net.IPv4zero, Port: 1},
remoteAddr: &net.UDPAddr{IP: net.IPv4zero, Port: 2},
readErr: io.EOF,
writeErr: errTestWriteFailed,
}
iceTransport := NewICETransport(nil, loggerFactory)
iceTransport.mux = mux.NewMux(mux.Config{
Conn: conn,
BufferSize: 1500,
LoggerFactory: loggerFactory,
})
defer func() { _ = iceTransport.mux.Close() }()
transport, err := api.NewDTLSTransport(iceTransport, nil)
assert.NoError(t, err)
assert.Equal(t, DTLSTransportStateNew, transport.State())
err = transport.Start(DTLSParameters{Role: DTLSRoleServer})
assert.Error(t, err)
assert.Equal(t, DTLSTransportStateFailed, transport.State())
assert.Nil(t, transport.conn)
assert.Equal(t, 2, reflect.ValueOf(iceTransport.mux).Elem().FieldByName("endpoints").Len())
}
func TestDTLSTransport_dtlsSharedOptions_IncludesOptionalOptions(t *testing.T) {
baseAPI := NewAPI()
baseTransport := &DTLSTransport{api: baseAPI}
baseCount := len(baseTransport.dtlsSharedOptions(tls.Certificate{}))
tests := []struct {
name string
configure func(*SettingEngine)
wantExtra int
}{
{
name: "CustomCipherSuites",
configure: func(se *SettingEngine) {
se.dtls.customCipherSuites = func() []dtls.CipherSuite {
return nil
}
},
wantExtra: 1,
},
{
name: "FlightInterval",
configure: func(se *SettingEngine) {
se.dtls.retransmissionInterval = time.Second
},
wantExtra: 1,
},
{
name: "ReplayProtectionWindow",
configure: func(se *SettingEngine) {
window := uint(1)
se.replayProtection.DTLS = &window
},
wantExtra: 1,
},
{
name: "CipherSuites",
configure: func(se *SettingEngine) {
se.dtls.cipherSuites = []dtls.CipherSuiteID{
dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
}
},
wantExtra: 1,
},
{
name: "EllipticCurves",
configure: func(se *SettingEngine) {
se.dtls.ellipticCurves = []dtlsElliptic.Curve{dtlsElliptic.P256}
},
wantExtra: 1,
},
{
name: "RootCAs",
configure: func(se *SettingEngine) {
se.dtls.rootCAs = x509.NewCertPool()
},
wantExtra: 1,
},
{
name: "KeyLogWriter",
configure: func(se *SettingEngine) {
se.dtls.keyLogWriter = &bytes.Buffer{}
},
wantExtra: 1,
},
{
name: "AllOptional",
configure: func(se *SettingEngine) {
se.dtls.customCipherSuites = func() []dtls.CipherSuite {
return nil
}
se.dtls.retransmissionInterval = time.Second
window := uint(1)
se.replayProtection.DTLS = &window
se.dtls.cipherSuites = []dtls.CipherSuiteID{
dtls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
}
se.dtls.ellipticCurves = []dtlsElliptic.Curve{dtlsElliptic.P256}
se.dtls.rootCAs = x509.NewCertPool()
se.dtls.keyLogWriter = &bytes.Buffer{}
},
wantExtra: 7,
},
{
name: "SupportedProtocols",
configure: func(se *SettingEngine) {
se.dtls.supportedProtocols = []string{"webrtc", "c-webrtc"}
},
wantExtra: 1,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
api := NewAPI()
tc.configure(api.settingEngine)
transport := &DTLSTransport{api: api}
opts := transport.dtlsSharedOptions(tls.Certificate{})
assert.Len(t, opts, baseCount+tc.wantExtra)
})
}
}
func TestDTLSTransport_toDTLSClientOptions_IncludesOptionalOptions(t *testing.T) {
baseAPI := NewAPI()
baseTransport := &DTLSTransport{api: baseAPI}
baseSharedOpts := baseTransport.dtlsSharedOptions(tls.Certificate{})
baseCount := len(baseTransport.toDTLSClientOptions(baseSharedOpts))
api := NewAPI()
api.settingEngine.dtls.clientHelloMessageHook = func(m handshake.MessageClientHello) handshake.Message {
return &m
}
transport := &DTLSTransport{api: api}
sharedOpts := transport.dtlsSharedOptions(tls.Certificate{})
opts := transport.toDTLSClientOptions(sharedOpts)
assert.Len(t, opts, baseCount+1)
}
func TestDTLSTransport_verifyPeerCertificateFunc_NoRemoteCertificate(t *testing.T) {
api := NewAPI()
transport := &DTLSTransport{api: api}
err := transport.verifyPeerCertificateFunc()(nil, nil)
assert.ErrorIs(t, err, errNoRemoteCertificate)
assert.Nil(t, transport.GetRemoteCertificate())
}
func TestDTLSTransport_verifyPeerCertificateFunc_ParseError(t *testing.T) {
api := NewAPI()
transport := &DTLSTransport{api: api}
rawCert := []byte("not a certificate")
err := transport.verifyPeerCertificateFunc()([][]byte{rawCert}, nil)
assert.Error(t, err)
assert.Equal(t, rawCert, transport.GetRemoteCertificate())
}
func TestDTLSTransport_toDTLSServerOptions_IncludesOptionalOptions(t *testing.T) {
baseAPI := NewAPI()
baseTransport := &DTLSTransport{api: baseAPI}
baseCount := len(baseTransport.toDTLSServerOptions(nil))
tests := []struct {
name string
configure func(*SettingEngine)
wantExtra int
}{
{
name: "ServerHelloMessageHook",
configure: func(se *SettingEngine) {
se.dtls.serverHelloMessageHook = func(m handshake.MessageServerHello) handshake.Message {
return &m
}
},
wantExtra: 1,
},
{
name: "CertificateRequestMessageHook",
configure: func(se *SettingEngine) {
se.dtls.certificateRequestMessageHook = func(m handshake.MessageCertificateRequest) handshake.Message {
return &m
}
},
wantExtra: 1,
},
{
name: "AllOptional",
configure: func(se *SettingEngine) {
se.dtls.serverHelloMessageHook = func(m handshake.MessageServerHello) handshake.Message {
return &m
}
se.dtls.certificateRequestMessageHook = func(m handshake.MessageCertificateRequest) handshake.Message {
return &m
}
},
wantExtra: 2,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
api := NewAPI()
tc.configure(api.settingEngine)
transport := &DTLSTransport{api: api}
opts := transport.toDTLSServerOptions(nil)
assert.Len(t, opts, baseCount+tc.wantExtra)
})
}
}
func TestDTLSTransport_handshakeDTLS_DeferredCancel(t *testing.T) {
lim := test.TimeOut(time.Second)
defer lim.Stop()
api := NewAPI()
transport := &DTLSTransport{api: api}
connectContextMakerCalled := false
cancelCalled := false
api.settingEngine.dtls.connectContextMaker = func() (context.Context, func()) {
connectContextMakerCalled = true
ctx, cancel := context.WithCancel(context.Background())
return ctx, func() {
cancelCalled = true
cancel()
}
}
packetConn := &failingPacketConn{
localAddr: &net.UDPAddr{IP: net.IPv4zero, Port: 1},
readErr: io.EOF,
writeErr: errTestWriteFailed,
}
dtlsConn, err := dtls.ClientWithOptions(packetConn, &net.UDPAddr{IP: net.IPv4zero, Port: 2})
assert.NoError(t, err)
defer func() { _ = dtlsConn.Close() }()
err = transport.handshakeDTLS(dtlsConn)
assert.Error(t, err)
assert.True(t, connectContextMakerCalled)
assert.True(t, cancelCalled)
}
func TestSRTPProtectionProfileFromDTLS(t *testing.T) {
tests := []struct {
name string
profile dtls.SRTPProtectionProfile
want srtp.ProtectionProfile
wantErr error
}{
{
name: "SRTP_AEAD_AES_128_GCM",
profile: dtls.SRTP_AEAD_AES_128_GCM,
want: srtp.ProtectionProfileAeadAes128Gcm,
},
{
name: "SRTP_AEAD_AES_256_GCM",
profile: dtls.SRTP_AEAD_AES_256_GCM,
want: srtp.ProtectionProfileAeadAes256Gcm,
},
{
name: "SRTP_AES128_CM_HMAC_SHA1_80",
profile: dtls.SRTP_AES128_CM_HMAC_SHA1_80,
want: srtp.ProtectionProfileAes128CmHmacSha1_80,
},
{
name: "SRTP_NULL_HMAC_SHA1_80",
profile: dtls.SRTP_NULL_HMAC_SHA1_80,
want: srtp.ProtectionProfileNullHmacSha1_80,
},
{
name: "Unknown",
profile: dtls.SRTPProtectionProfile(255),
wantErr: ErrNoSRTPProtectionProfile,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := srtpProtectionProfileFromDTLS(tc.profile)
if tc.wantErr != nil {
assert.ErrorIs(t, err, tc.wantErr)
return
}
assert.NoError(t, err)
assert.Equal(t, tc.want, got)
})
}
}
================================================
FILE: dtlstransportstate.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
// DTLSTransportState indicates the DTLS transport establishment state.
type DTLSTransportState int
const (
// DTLSTransportStateUnknown is the enum's zero-value.
DTLSTransportStateUnknown DTLSTransportState = iota
// DTLSTransportStateNew indicates that DTLS has not started negotiating
// yet.
DTLSTransportStateNew
// 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 DTLSTransportStateUnknown
}
}
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()
}
}
// MarshalText implements encoding.TextMarshaler.
func (t DTLSTransportState) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (t *DTLSTransportState) UnmarshalText(b []byte) error {
*t = newDTLSTransportState(string(b))
return nil
}
================================================
FILE: dtlstransportstate_test.go
================================================
// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewDTLSTransportState(t *testing.T) {
testCases := []struct {
stateString string
expectedState DTLSTransportState
}{
{ErrUnknownType.Error(), DTLSTransportStateUnknown},
{"new", DTLSTransportStateNew},
{"connecting", DTLSTransportStateConnecting},
{"connected", DTLSTransportStateConnected},
{"closed", DTLSTransportStateClosed},
{"failed", DTLSTransportStateFailed},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedState,
newDTLSTransportState(testCase.stateString),
"testCase: %d %v", i, testCase,
)
}
}
func TestDTLSTransportState_String(t *testing.T) {
testCases := []struct {
state DTLSTransportState
expectedString string
}{
{DTLSTransportStateUnknown, ErrUnknownType.Error()},
{DTLSTransportStateNew, "new"},
{DTLSTransportStateConnecting, "connecting"},
{DTLSTransportStateConnected, "connected"},
{DTLSTransportStateClosed, "closed"},
{DTLSTransportStateFailed, "failed"},
}
for i, testCase := range testCases {
assert.Equal(t,
testCase.expectedString,
testCase.state.String(),
"testCase: %d %v", i, test
gitextract_laa6g7n2/ ├── .codacy.yaml ├── .eslintrc.json ├── .github/ │ ├── .ci.conf │ ├── .gitignore │ ├── fetch-scripts.sh │ ├── install-hooks.sh │ ├── pion-gopher-webrtc.png.license │ └── workflows/ │ ├── api.yaml │ ├── browser-e2e.yaml │ ├── codeql-analysis.yml │ ├── examples-tests.yaml │ ├── fuzz.yaml │ ├── lint.yaml │ ├── release.yml │ ├── renovate-go-sum-fix.yaml │ ├── reuse.yml │ ├── standardjs.yaml │ ├── test.yaml │ └── tidy-check.yaml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .reuse/ │ └── dep5 ├── DESIGN.md ├── LICENSE ├── LICENSES/ │ └── MIT.txt ├── README.md ├── api.go ├── api_js.go ├── api_test.go ├── bundlepolicy.go ├── bundlepolicy_test.go ├── certificate.go ├── certificate_js_test.go ├── certificate_test.go ├── codecov.yml ├── configuration.go ├── configuration_common.go ├── configuration_js.go ├── configuration_test.go ├── constants.go ├── datachannel.go ├── datachannel_go_test.go ├── datachannel_js.go ├── datachannel_js_detach.go ├── datachannel_test.go ├── datachannelinit.go ├── datachannelmessage.go ├── datachannelparameters.go ├── datachannelstate.go ├── datachannelstate_test.go ├── dtlsfingerprint.go ├── dtlsparameters.go ├── dtlsrole.go ├── dtlsrole_test.go ├── dtlstransport.go ├── dtlstransport_js.go ├── dtlstransport_test.go ├── dtlstransportstate.go ├── dtlstransportstate_test.go ├── e2e/ │ ├── Dockerfile │ ├── e2e_test.go │ └── test.html ├── errors.go ├── examples/ │ ├── README.md │ ├── bandwidth-estimation-from-disk/ │ │ ├── README.md │ │ └── main.go │ ├── broadcast/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── custom-logger/ │ │ ├── README.md │ │ └── main.go │ ├── data-channels/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ ├── demo.js │ │ │ └── main.go │ │ └── main.go │ ├── data-channels-detach/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.html │ │ │ └── main.go │ │ └── main.go │ ├── data-channels-detach-create/ │ │ ├── README.md │ │ └── main.go │ ├── data-channels-flow-control/ │ │ ├── README.md │ │ └── main.go │ ├── data-channels-simple/ │ │ ├── README.md │ │ ├── demo.html │ │ └── main.go │ ├── data-channels-whip-whep-like/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── example.html │ ├── examples.go │ ├── examples.json │ ├── ice-proxy/ │ │ ├── README.md │ │ ├── answer.go │ │ ├── main.go │ │ ├── offer.go │ │ ├── proxy.go │ │ └── turn.go │ ├── ice-restart/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── ice-single-port/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── ice-tcp/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── index.html │ ├── insertable-streams/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── ortc/ │ │ ├── README.md │ │ └── main.go │ ├── ortc-media/ │ │ ├── README.md │ │ └── main.go │ ├── pion-to-pion/ │ │ ├── README.md │ │ ├── answer/ │ │ │ ├── Dockerfile │ │ │ └── main.go │ │ ├── docker-compose.yml │ │ ├── offer/ │ │ │ ├── Dockerfile │ │ │ └── main.go │ │ └── test.sh │ ├── play-from-disk/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── play-from-disk-fec/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── play-from-disk-playlist-control/ │ │ ├── README.md │ │ ├── main.go │ │ └── web/ │ │ ├── app.css │ │ ├── app.js │ │ └── index.html │ ├── play-from-disk-renegotiation/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── quick-switch/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── reflect/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── repacketize/ │ │ ├── README.md │ │ ├── index.html │ │ ├── index.js │ │ └── main.go │ ├── rtcp-processing/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── rtp-forwarder/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ ├── main.go │ │ ├── rtp-forwarder.sdp │ │ └── rtp-forwarder.sdp.license │ ├── rtp-to-webrtc/ │ │ ├── README.md │ │ └── main.go │ ├── save-to-disk/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── save-to-disk-av1/ │ │ ├── README.md │ │ └── main.go │ ├── simulcast/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── stats/ │ │ ├── README.md │ │ └── main.go │ ├── swap-tracks/ │ │ ├── README.md │ │ ├── jsfiddle/ │ │ │ ├── demo.css │ │ │ ├── demo.details │ │ │ ├── demo.html │ │ │ └── demo.js │ │ └── main.go │ ├── trickle-ice/ │ │ ├── README.md │ │ ├── index.html │ │ └── main.go │ ├── vnet/ │ │ ├── README.md │ │ └── show-network-usage/ │ │ └── main.go │ ├── warp/ │ │ ├── index.html │ │ └── main.go │ └── whip-whep/ │ ├── README.md │ ├── index.html │ └── main.go ├── gathering_complete_promise.go ├── gathering_complete_promise_example_test.go ├── go.mod ├── go.sum ├── ice_go.go ├── icecandidate.go ├── icecandidate_test.go ├── icecandidateinit.go ├── icecandidateinit_test.go ├── icecandidatepair.go ├── icecandidatepair_test.go ├── icecandidatetype.go ├── icecandidatetype_test.go ├── icecomponent.go ├── icecomponent_test.go ├── iceconnectionstate.go ├── iceconnectionstate_test.go ├── icecredentialtype.go ├── icecredentialtype_test.go ├── icegatherer.go ├── icegatherer_test.go ├── icegathererstate.go ├── icegathererstate_test.go ├── icegatheringstate.go ├── icegatheringstate_test.go ├── icegatheroptions.go ├── icemux.go ├── iceparameters.go ├── iceprotocol.go ├── iceprotocol_test.go ├── icerole.go ├── icerole_test.go ├── iceserver.go ├── iceserver_js.go ├── iceserver_test.go ├── icetransport.go ├── icetransport_js.go ├── icetransport_test.go ├── icetransportpolicy.go ├── icetransportpolicy_test.go ├── icetransportstate.go ├── icetransportstate_test.go ├── interceptor.go ├── interceptor_option.go ├── interceptor_test.go ├── internal/ │ ├── fmtp/ │ │ ├── av1.go │ │ ├── fmtp.go │ │ ├── fmtp_test.go │ │ ├── h264.go │ │ └── vp9.go │ ├── mux/ │ │ ├── endpoint.go │ │ ├── mux.go │ │ ├── mux_test.go │ │ └── muxfunc.go │ └── util/ │ ├── util.go │ └── util_test.go ├── js_utils.go ├── mediaengine.go ├── mediaengine_test.go ├── mimetype.go ├── networktype.go ├── networktype_test.go ├── oauthcredential.go ├── offeransweroptions.go ├── operations.go ├── operations_test.go ├── ortc_datachannel_test.go ├── ortc_media_test.go ├── ortc_test.go ├── package.json ├── peerconnection.go ├── peerconnection_close_test.go ├── peerconnection_go_test.go ├── peerconnection_js.go ├── peerconnection_js_test.go ├── peerconnection_media_test.go ├── peerconnection_renegotiation_test.go ├── peerconnection_test.go ├── peerconnectionstate.go ├── peerconnectionstate_test.go ├── pkg/ │ ├── media/ │ │ ├── h264reader/ │ │ │ ├── h264reader.go │ │ │ ├── h264reader_test.go │ │ │ └── nalunittype.go │ │ ├── h264writer/ │ │ │ ├── h264writer.go │ │ │ └── h264writer_test.go │ │ ├── h265reader/ │ │ │ ├── h265reader.go │ │ │ ├── h265reader_test.go │ │ │ └── nalunittype.go │ │ ├── h265writer/ │ │ │ ├── h265writer.go │ │ │ └── h265writer_test.go │ │ ├── ivfreader/ │ │ │ ├── ivfreader.go │ │ │ └── ivfreader_test.go │ │ ├── ivfwriter/ │ │ │ ├── ivfwriter.go │ │ │ └── ivfwriter_test.go │ │ ├── media.go │ │ ├── media_test.go │ │ ├── oggreader/ │ │ │ ├── oggreader.go │ │ │ └── oggreader_test.go │ │ ├── oggwriter/ │ │ │ ├── oggwriter.go │ │ │ └── oggwriter_test.go │ │ ├── rtpdump/ │ │ │ ├── reader.go │ │ │ ├── reader_test.go │ │ │ ├── rtpdump.go │ │ │ ├── rtpdump_test.go │ │ │ ├── writer.go │ │ │ └── writer_test.go │ │ └── samplebuilder/ │ │ ├── sampleSequenceLocation.go │ │ ├── sampleSequenceLocation_test.go │ │ ├── samplebuilder.go │ │ └── samplebuilder_test.go │ ├── null/ │ │ ├── null.go │ │ └── null_test.go │ └── rtcerr/ │ └── errors.go ├── renovate.json ├── rtcpfeedback.go ├── rtcpmuxpolicy.go ├── rtcpmuxpolicy_test.go ├── rtpcapabilities.go ├── rtpcodec.go ├── rtpcodec_test.go ├── rtpcodingparameters.go ├── rtpdecodingparameters.go ├── rtpencodingparameters.go ├── rtpreceiveparameters.go ├── rtpreceiver.go ├── rtpreceiver_go.go ├── rtpreceiver_go_test.go ├── rtpreceiver_js.go ├── rtpreceiver_test.go ├── rtpsender.go ├── rtpsender_js.go ├── rtpsender_test.go ├── rtpsendparameters.go ├── rtptransceiver.go ├── rtptransceiver_js.go ├── rtptransceiver_test.go ├── rtptransceiverdirection.go ├── rtptransceiverdirection_test.go ├── rtptransceiverinit.go ├── rtptransceiverinit_go_test.go ├── sctpcapabilities.go ├── sctptransport.go ├── sctptransport_js.go ├── sctptransport_test.go ├── sctptransportstate.go ├── sctptransportstate_test.go ├── sdp.go ├── sdp_test.go ├── sdpsemantics.go ├── sdpsemantics_test.go ├── sdptype.go ├── sdptype_test.go ├── sessiondescription.go ├── sessiondescription_test.go ├── settingengine.go ├── settingengine_js.go ├── settingengine_test.go ├── signalingstate.go ├── signalingstate_test.go ├── srtp_writer_future.go ├── srtp_writer_future_test.go ├── stats.go ├── stats_go.go ├── stats_go_test.go ├── test-wasm/ │ ├── LICENSE │ ├── go_js_wasm_exec │ └── node_shim.js ├── track_local.go ├── track_local_static.go ├── track_local_static_test.go ├── track_remote.go ├── track_remote_test.go ├── vnet_test.go └── webrtc.go
Showing preview only (258K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2683 symbols across 247 files)
FILE: api.go
type API (line 18) | type API struct
function NewAPI (line 30) | func NewAPI(options ...func(*API)) *API {
function WithMediaEngine (line 70) | func WithMediaEngine(m *MediaEngine) func(a *API) {
function WithSettingEngine (line 81) | func WithSettingEngine(s SettingEngine) func(a *API) {
function WithInterceptorRegistry (line 89) | func WithInterceptorRegistry(ir *interceptor.Registry) func(a *API) {
FILE: api_js.go
type API (line 10) | type API struct
function NewAPI (line 15) | func NewAPI(options ...func(*API)) *API {
function WithSettingEngine (line 31) | func WithSettingEngine(s SettingEngine) func(a *API) {
FILE: api_test.go
function TestNewAPI (line 14) | func TestNewAPI(t *testing.T) {
function TestNewAPI_Options (line 21) | func TestNewAPI_Options(t *testing.T) {
function TestNewAPI_OptionsDefaultize (line 34) | func TestNewAPI_OptionsDefaultize(t *testing.T) {
FILE: bundlepolicy.go
type BundlePolicy (line 14) | type BundlePolicy
method String (line 57) | func (t BundlePolicy) String() string {
method UnmarshalJSON (line 71) | func (t *BundlePolicy) UnmarshalJSON(b []byte) error {
method MarshalJSON (line 83) | func (t BundlePolicy) MarshalJSON() ([]byte, error) {
constant BundlePolicyUnknown (line 18) | BundlePolicyUnknown BundlePolicy = iota
constant BundlePolicyBalanced (line 24) | BundlePolicyBalanced
constant BundlePolicyMaxCompat (line 29) | BundlePolicyMaxCompat
constant BundlePolicyMaxBundle (line 34) | BundlePolicyMaxBundle
constant bundlePolicyBalancedStr (line 39) | bundlePolicyBalancedStr = "balanced"
constant bundlePolicyMaxCompatStr (line 40) | bundlePolicyMaxCompatStr = "max-compat"
constant bundlePolicyMaxBundleStr (line 41) | bundlePolicyMaxBundleStr = "max-bundle"
function newBundlePolicy (line 44) | func newBundlePolicy(raw string) BundlePolicy {
FILE: bundlepolicy_test.go
function TestNewBundlePolicy (line 12) | func TestNewBundlePolicy(t *testing.T) {
function TestBundlePolicy_String (line 32) | func TestBundlePolicy_String(t *testing.T) {
FILE: certificate.go
type Certificate (line 25) | type Certificate struct
method Equals (line 71) | func (c Certificate) Equals(cert Certificate) bool {
method Expires (line 99) | func (c Certificate) Expires() time.Time {
method GetFingerprints (line 109) | func (c Certificate) GetFingerprints() ([]DTLSFingerprint, error) {
method collectStats (line 164) | func (c Certificate) collectStats(report *statsReportCollector) error {
method PEM (line 244) | func (c Certificate) PEM() (string, error) {
function NewCertificate (line 35) | func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certi...
function GenerateCertificate (line 136) | func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, err...
function CertificateFromX509 (line 160) | func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509...
function CertificateFromPEM (line 191) | func CertificateFromPEM(pems string) (*Certificate, error) { //nolint: c...
FILE: certificate_js_test.go
function TestGenerateCertificateRSA (line 23) | func TestGenerateCertificateRSA(t *testing.T) {
function TestGenerateCertificateECDSA (line 44) | func TestGenerateCertificateECDSA(t *testing.T) {
function TestGenerateCertificateEqual (line 68) | func TestGenerateCertificateEqual(t *testing.T) {
function TestGenerateCertificateExpires (line 92) | func TestGenerateCertificateExpires(t *testing.T) {
function TestPEM (line 107) | func TestPEM(t *testing.T) {
constant certHeader (line 123) | certHeader = `!! This is a test certificate: Don't use it in production !!
constant certPriv (line 131) | certPriv = `-----BEGIN PRIVATE KEY-----
constant certCert (line 138) | certCert = `-----BEGIN CERTIFICATE-----
function TestOpensslCert (line 152) | func TestOpensslCert(t *testing.T) {
function TestEmpty (line 158) | func TestEmpty(t *testing.T) {
function TestMultiCert (line 164) | func TestMultiCert(t *testing.T) {
function TestMultiPriv (line 170) | func TestMultiPriv(t *testing.T) {
FILE: certificate_test.go
function TestGenerateCertificateRSA (line 22) | func TestGenerateCertificateRSA(t *testing.T) {
function TestGenerateCertificateECDSA (line 43) | func TestGenerateCertificateECDSA(t *testing.T) {
function TestGenerateCertificateEqual (line 67) | func TestGenerateCertificateEqual(t *testing.T) {
function TestGenerateCertificateExpires (line 91) | func TestGenerateCertificateExpires(t *testing.T) {
function TestPEM (line 106) | func TestPEM(t *testing.T) {
constant certHeader (line 122) | certHeader = `!! This is a test certificate: Don't use it in production !!
constant certPriv (line 130) | certPriv = `-----BEGIN PRIVATE KEY-----
constant certCert (line 137) | certCert = `-----BEGIN CERTIFICATE-----
function TestOpensslCert (line 151) | func TestOpensslCert(t *testing.T) {
function TestEmpty (line 157) | func TestEmpty(t *testing.T) {
function TestMultiCert (line 163) | func TestMultiCert(t *testing.T) {
function TestMultiPriv (line 169) | func TestMultiPriv(t *testing.T) {
FILE: configuration.go
type Configuration (line 13) | type Configuration struct
FILE: configuration_common.go
method getICEServers (line 11) | func (c Configuration) getICEServers() []ICEServer {
FILE: configuration_js.go
type Configuration (line 12) | type Configuration struct
FILE: configuration_test.go
function TestConfiguration_getICEServers (line 13) | func TestConfiguration_getICEServers(t *testing.T) {
function TestConfigurationJSON (line 45) | func TestConfigurationJSON(t *testing.T) {
FILE: constants.go
constant receiveMTU (line 15) | receiveMTU = 1500
constant simulcastProbeCount (line 20) | simulcastProbeCount = 10
constant simulcastMaxProbeRoutines (line 24) | simulcastMaxProbeRoutines = 25
constant defaultMaxSCTPMessageSize (line 28) | defaultMaxSCTPMessageSize = 1073741823
constant sctpMaxMessageSizeUnsetValue (line 33) | sctpMaxMessageSizeUnsetValue = math.MaxUint16
constant mediaSectionApplication (line 35) | mediaSectionApplication = "application"
constant sdpAttributeRid (line 37) | sdpAttributeRid = "rid"
constant sdpAttributeSimulcast (line 39) | sdpAttributeSimulcast = "simulcast"
constant outboundMTU (line 41) | outboundMTU = 1200
constant rtpPayloadTypeBitmask (line 43) | rtpPayloadTypeBitmask = 0x7F
constant incomingUnhandledRTPSsrc (line 45) | incomingUnhandledRTPSsrc = "Incoming unhandled RTP ssrc(%d), OnTrack wil...
constant useReadSimulcast (line 47) | useReadSimulcast = "Use ReadSimulcast(rid) instead of Read() when multip...
constant generatedCertificateOrigin (line 49) | generatedCertificateOrigin = "WebRTC"
constant AttributeRtxPayloadType (line 53) | AttributeRtxPayloadType = "rtx_payload_type"
constant AttributeRtxSsrc (line 56) | AttributeRtxSsrc = "rtx_ssrc"
constant AttributeRtxSequenceNumber (line 59) | AttributeRtxSequenceNumber = "rtx_sequence_number"
function defaultSrtpProtectionProfiles (line 62) | func defaultSrtpProtectionProfiles() []dtls.SRTPProtectionProfile {
FILE: datachannel.go
type DataChannel (line 26) | type DataChannel struct
method open (line 117) | func (d *DataChannel) open(sctpTransport *SCTPTransport) error { //nol...
method Transport (line 197) | func (d *DataChannel) Transport() *SCTPTransport {
method checkDetachAfterOpen (line 206) | func (d *DataChannel) checkDetachAfterOpen() {
method OnOpen (line 217) | func (d *DataChannel) OnOpen(f func()) {
method onOpen (line 232) | func (d *DataChannel) onOpen() {
method OnDial (line 252) | func (d *DataChannel) OnDial(f func()) {
method onDial (line 264) | func (d *DataChannel) onDial() {
method OnClose (line 285) | func (d *DataChannel) OnClose(f func()) {
method onClose (line 291) | func (d *DataChannel) onClose() {
method OnMessage (line 307) | func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
method onMessage (line 313) | func (d *DataChannel) onMessage(msg DataChannelMessage) {
method handleOpen (line 329) | func (d *DataChannel) handleOpen(dc *datachannel.DataChannel, isRemote...
method OnError (line 376) | func (d *DataChannel) OnError(f func(err error)) {
method onError (line 382) | func (d *DataChannel) onError(err error) {
method readLoop (line 397) | func (d *DataChannel) readLoop() {
method Send (line 439) | func (d *DataChannel) Send(data []byte) error {
method SendText (line 451) | func (d *DataChannel) SendText(s string) error {
method ensureOpen (line 462) | func (d *DataChannel) ensureOpen() error {
method Detach (line 483) | func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
method DetachWithDeadline (line 489) | func (d *DataChannel) DetachWithDeadline() (datachannel.ReadWriteClose...
method Close (line 532) | func (d *DataChannel) Close() error {
method GracefulClose (line 540) | func (d *DataChannel) GracefulClose() error {
method close (line 550) | func (d *DataChannel) close(shouldGracefullyClose bool) error {
method Label (line 577) | func (d *DataChannel) Label() string {
method Ordered (line 586) | func (d *DataChannel) Ordered() bool {
method MaxPacketLifeTime (line 595) | func (d *DataChannel) MaxPacketLifeTime() *uint16 {
method MaxRetransmits (line 604) | func (d *DataChannel) MaxRetransmits() *uint16 {
method Protocol (line 613) | func (d *DataChannel) Protocol() string {
method Negotiated (line 622) | func (d *DataChannel) Negotiated() bool {
method ID (line 635) | func (d *DataChannel) ID() *uint16 {
method ReadyState (line 643) | func (d *DataChannel) ReadyState() DataChannelState {
method BufferedAmount (line 661) | func (d *DataChannel) BufferedAmount() uint64 {
method BufferedAmountLowThreshold (line 678) | func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
method SetBufferedAmountLowThreshold (line 691) | func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
method OnBufferedAmountLow (line 705) | func (d *DataChannel) OnBufferedAmountLow(f func()) {
method makeBufferedAmountLowHandler (line 717) | func (d *DataChannel) makeBufferedAmountLowHandler(f func()) func() {
method getStatsID (line 729) | func (d *DataChannel) getStatsID() string {
method collectStats (line 736) | func (d *DataChannel) collectStats(collector *statsReportCollector) {
method setReadyState (line 766) | func (d *DataChannel) setReadyState(r DataChannelState) {
method NewDataChannel (line 71) | func (api *API) NewDataChannel(transport *SCTPTransport, params *DataCha...
method newDataChannel (line 87) | func (api *API) newDataChannel(
FILE: datachannel_go_test.go
function TestDataChannel_EventHandlers (line 27) | func TestDataChannel_EventHandlers(t *testing.T) {
function TestDataChannel_MessagesAreOrdered (line 67) | func TestDataChannel_MessagesAreOrdered(t *testing.T) {
function TestDataChannelParamters_Go (line 123) | func TestDataChannelParamters_Go(t *testing.T) {
function TestDataChannelBufferedAmount (line 179) | func TestDataChannelBufferedAmount(t *testing.T) { //nolint:cyclop
function TestEOF (line 338) | func TestEOF(t *testing.T) { //nolint:cyclop
function TestDataChannel_NonStandardSessionDescription (line 513) | func TestDataChannel_NonStandardSessionDescription(t *testing.T) {
function TestDataChannel_Dial (line 567) | func TestDataChannel_Dial(t *testing.T) {
function TestDetachRemovesDatachannelReference (line 634) | func TestDetachRemovesDatachannelReference(t *testing.T) {
function TestDataChannelClose (line 677) | func TestDataChannelClose(t *testing.T) {
function TestDataChannel_DetachErrors (line 754) | func TestDataChannel_DetachErrors(t *testing.T) {
function TestDataChannelMessageSize (line 781) | func TestDataChannelMessageSize(t *testing.T) {
function TestOnBufferedAmountLowDeadlock (line 816) | func TestOnBufferedAmountLowDeadlock(t *testing.T) {
function TestOnBufferedAmountLowRespectsReadyState (line 858) | func TestOnBufferedAmountLowRespectsReadyState(t *testing.T) {
FILE: datachannel_js.go
constant dataChannelBufferSize (line 17) | dataChannelBufferSize = 16384
type DataChannel (line 22) | type DataChannel struct
method JSValue (line 40) | func (d *DataChannel) JSValue() js.Value {
method OnOpen (line 46) | func (d *DataChannel) OnOpen(f func()) {
method OnClose (line 61) | func (d *DataChannel) OnClose(f func()) {
method OnClosing (line 76) | func (d *DataChannel) OnClosing(f func()) {
method OnError (line 89) | func (d *DataChannel) OnError(f func(err error)) {
method OnMessage (line 109) | func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
method Send (line 131) | func (d *DataChannel) Send(data []byte) (err error) {
method SendText (line 144) | func (d *DataChannel) SendText(s string) (err error) {
method Detach (line 162) | func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
method Close (line 173) | func (d *DataChannel) Close() (err error) {
method Label (line 208) | func (d *DataChannel) Label() string {
method Ordered (line 214) | func (d *DataChannel) Ordered() bool {
method MaxPacketLifeTime (line 224) | func (d *DataChannel) MaxPacketLifeTime() *uint16 {
method MaxRetransmits (line 236) | func (d *DataChannel) MaxRetransmits() *uint16 {
method Protocol (line 242) | func (d *DataChannel) Protocol() string {
method Negotiated (line 248) | func (d *DataChannel) Negotiated() bool {
method ID (line 257) | func (d *DataChannel) ID() *uint16 {
method ReadyState (line 262) | func (d *DataChannel) ReadyState() DataChannelState {
method BufferedAmount (line 276) | func (d *DataChannel) BufferedAmount() uint64 {
method BufferedAmountLowThreshold (line 285) | func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
method SetBufferedAmountLowThreshold (line 291) | func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
method OnBufferedAmountLow (line 298) | func (d *DataChannel) OnBufferedAmountLow(f func()) {
function valueToDataChannelMessage (line 318) | func valueToDataChannelMessage(val js.Value) DataChannelMessage {
FILE: datachannel_js_detach.go
type detachedDataChannel (line 13) | type detachedDataChannel struct
method Read (line 38) | func (c *detachedDataChannel) Read(p []byte) (int, error) {
method ReadDataChannel (line 43) | func (c *detachedDataChannel) ReadDataChannel(p []byte) (int, bool, er...
method Write (line 56) | func (c *detachedDataChannel) Write(p []byte) (n int, err error) {
method WriteDataChannel (line 60) | func (c *detachedDataChannel) WriteDataChannel(p []byte, isString bool...
method Close (line 71) | func (c *detachedDataChannel) Close() error {
function newDetachedDataChannel (line 20) | func newDetachedDataChannel(dc *DataChannel) *detachedDataChannel {
FILE: datachannel_test.go
constant expectedLabel (line 21) | expectedLabel = "data"
function closePairNow (line 23) | func closePairNow(tb testing.TB, pc1, pc2 io.Closer) {
function closePair (line 40) | func closePair(t *testing.T, pc1, pc2 io.Closer, done <-chan bool) {
function setUpDataChannelParametersTest (line 51) | func setUpDataChannelParametersTest(
function closeReliabilityParamTest (line 67) | func closeReliabilityParamTest(t *testing.T, pc1, pc2 *PeerConnection, d...
function BenchmarkDataChannelSend2 (line 76) | func BenchmarkDataChannelSend2(b *testing.B) { benchmarkDataChannelSend...
function BenchmarkDataChannelSend4 (line 77) | func BenchmarkDataChannelSend4(b *testing.B) { benchmarkDataChannelSend...
function BenchmarkDataChannelSend8 (line 78) | func BenchmarkDataChannelSend8(b *testing.B) { benchmarkDataChannelSend...
function BenchmarkDataChannelSend16 (line 79) | func BenchmarkDataChannelSend16(b *testing.B) { benchmarkDataChannelSend...
function BenchmarkDataChannelSend32 (line 80) | func BenchmarkDataChannelSend32(b *testing.B) { benchmarkDataChannelSend...
function benchmarkDataChannelSend (line 83) | func benchmarkDataChannelSend(b *testing.B, numChannels int) {
function TestDataChannel_Open (line 124) | func TestDataChannel_Open(t *testing.T) {
function TestDataChannel_Send (line 220) | func TestDataChannel_Send(t *testing.T) { //nolint:cyclop
function TestDataChannel_Close (line 312) | func TestDataChannel_Close(t *testing.T) {
function TestDataChannelParameters (line 339) | func TestDataChannelParameters(t *testing.T) { //nolint:cyclop
FILE: datachannelinit.go
type DataChannelInit (line 8) | type DataChannelInit struct
FILE: datachannelmessage.go
type DataChannelMessage (line 10) | type DataChannelMessage struct
FILE: datachannelparameters.go
type DataChannelParameters (line 7) | type DataChannelParameters struct
FILE: datachannelstate.go
type DataChannelState (line 7) | type DataChannelState
method String (line 54) | func (t DataChannelState) String() string {
method MarshalText (line 70) | func (t DataChannelState) MarshalText() ([]byte, error) {
method UnmarshalText (line 75) | func (t *DataChannelState) UnmarshalText(b []byte) error {
constant DataChannelStateUnknown (line 11) | DataChannelStateUnknown DataChannelState = iota
constant DataChannelStateConnecting (line 16) | DataChannelStateConnecting
constant DataChannelStateOpen (line 20) | DataChannelStateOpen
constant DataChannelStateClosing (line 24) | DataChannelStateClosing
constant DataChannelStateClosed (line 28) | DataChannelStateClosed
constant dataChannelStateConnectingStr (line 33) | dataChannelStateConnectingStr = "connecting"
constant dataChannelStateOpenStr (line 34) | dataChannelStateOpenStr = "open"
constant dataChannelStateClosingStr (line 35) | dataChannelStateClosingStr = "closing"
constant dataChannelStateClosedStr (line 36) | dataChannelStateClosedStr = "closed"
function newDataChannelState (line 39) | func newDataChannelState(raw string) DataChannelState {
FILE: datachannelstate_test.go
function TestNewDataChannelState (line 12) | func TestNewDataChannelState(t *testing.T) {
function TestDataChannelState_String (line 33) | func TestDataChannelState_String(t *testing.T) {
FILE: dtlsfingerprint.go
type DTLSFingerprint (line 8) | type DTLSFingerprint struct
FILE: dtlsparameters.go
type DTLSParameters (line 7) | type DTLSParameters struct
FILE: dtlsrole.go
type DTLSRole (line 11) | type DTLSRole
method String (line 48) | func (r DTLSRole) String() string {
constant DTLSRoleUnknown (line 15) | DTLSRoleUnknown DTLSRole = iota
constant DTLSRoleAuto (line 20) | DTLSRoleAuto
constant DTLSRoleClient (line 23) | DTLSRoleClient
constant DTLSRoleServer (line 26) | DTLSRoleServer
constant defaultDtlsRoleAnswer (line 39) | defaultDtlsRoleAnswer = DTLSRoleClient
constant defaultDtlsRoleOffer (line 45) | defaultDtlsRoleOffer = DTLSRoleAuto
function dtlsRoleFromSDP (line 63) | func dtlsRoleFromSDP(sessionDescription *sdp.SessionDescription) DTLSRole {
function connectionRoleFromDtlsRole (line 86) | func connectionRoleFromDtlsRole(d DTLSRole) sdp.ConnectionRole {
FILE: dtlsrole_test.go
function TestDTLSRole_String (line 14) | func TestDTLSRole_String(t *testing.T) {
function TestDTLSRoleFromSDP (line 34) | func TestDTLSRoleFromSDP(t *testing.T) {
FILE: dtlstransport.go
type DTLSTransport (line 36) | type DTLSTransport struct
method ICETransport (line 112) | func (t *DTLSTransport) ICETransport() *ICETransport {
method onStateChange (line 120) | func (t *DTLSTransport) onStateChange(state DTLSTransportState) {
method OnStateChange (line 130) | func (t *DTLSTransport) OnStateChange(f func(DTLSTransportState)) {
method State (line 137) | func (t *DTLSTransport) State() DTLSTransportState {
method WriteRTCP (line 146) | func (t *DTLSTransport) WriteRTCP(pkts []rtcp.Packet) (int, error) {
method GetLocalParameters (line 167) | func (t *DTLSTransport) GetLocalParameters() (DTLSParameters, error) {
method GetRemoteCertificate (line 187) | func (t *DTLSTransport) GetRemoteCertificate() []byte {
method startSRTP (line 194) | func (t *DTLSTransport) startSRTP() error {
method getSRTPSession (line 259) | func (t *DTLSTransport) getSRTPSession() (*srtp.SessionSRTP, error) {
method getSRTCPSession (line 267) | func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) {
method role (line 275) | func (t *DTLSTransport) role() DTLSRole {
method Start (line 303) | func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error {
method prepareStart (line 339) | func (t *DTLSTransport) prepareStart(remoteParameters DTLSParameters) ...
method dtlsSharedOptions (line 366) | func (t *DTLSTransport) dtlsSharedOptions(certificate tls.Certificate)...
method srtpProtectionProfiles (line 429) | func (t *DTLSTransport) srtpProtectionProfiles() []dtls.SRTPProtection...
method verifyPeerCertificateFunc (line 437) | func (t *DTLSTransport) verifyPeerCertificateFunc() func([][]byte, [][...
method connectDTLS (line 460) | func (t *DTLSTransport) connectDTLS(
method toDTLSServerOptions (line 484) | func (t *DTLSTransport) toDTLSServerOptions(sharedOpts []dtls.Option) ...
method toDTLSClientOptions (line 518) | func (t *DTLSTransport) toDTLSClientOptions(sharedOpts []dtls.Option) ...
method handshakeDTLS (line 534) | func (t *DTLSTransport) handshakeDTLS(dtlsConn *dtls.Conn) error {
method completeStart (line 547) | func (t *DTLSTransport) completeStart(dtlsConn *dtls.Conn) error {
method failStart (line 566) | func (t *DTLSTransport) failStart(err error) error {
method Stop (line 599) | func (t *DTLSTransport) Stop() error {
method validateFingerPrint (line 630) | func (t *DTLSTransport) validateFingerPrint(remoteCert *x509.Certifica...
method ensureICEConn (line 650) | func (t *DTLSTransport) ensureICEConn() error {
method storeSimulcastStream (line 658) | func (t *DTLSTransport) storeSimulcastStream(
method streamsForSSRC (line 668) | func (t *DTLSTransport) streamsForSSRC(
type simulcastStreamPair (line 62) | type simulcastStreamPair struct
type streamsForSSRCResult (line 67) | type streamsForSSRCResult struct
method NewDTLSTransport (line 77) | func (api *API) NewDTLSTransport(transport *ICETransport, certificates [...
function srtpProtectionProfileFromDTLSConn (line 574) | func srtpProtectionProfileFromDTLSConn(dtlsConn *dtls.Conn) (srtp.Protec...
function srtpProtectionProfileFromDTLS (line 583) | func srtpProtectionProfileFromDTLS(srtpProfile dtls.SRTPProtectionProfil...
FILE: dtlstransport_js.go
type DTLSTransport (line 15) | type DTLSTransport struct
method JSValue (line 21) | func (r *DTLSTransport) JSValue() js.Value {
method ICETransport (line 27) | func (r *DTLSTransport) ICETransport() *ICETransport {
method GetRemoteCertificate (line 38) | func (t *DTLSTransport) GetRemoteCertificate() []byte {
FILE: dtlstransport_test.go
function TestInvalidFingerprintCausesFailed (line 31) | func TestInvalidFingerprintCausesFailed(t *testing.T) { //nolint:cyclop
function TestPeerConnection_DTLSRoleSettingEngine (line 151) | func TestPeerConnection_DTLSRoleSettingEngine(t *testing.T) {
type errConn (line 180) | type errConn struct
method Read (line 187) | func (c *errConn) Read([]byte) (int, error) { return 0, c.read...
method Write (line 188) | func (c *errConn) Write([]byte) (int, error) { return 0, c.writ...
method Close (line 189) | func (c *errConn) Close() error { return nil }
method LocalAddr (line 190) | func (c *errConn) LocalAddr() net.Addr { return c.localAd...
method RemoteAddr (line 191) | func (c *errConn) RemoteAddr() net.Addr { return c.remoteA...
method SetDeadline (line 192) | func (c *errConn) SetDeadline(time.Time) error { return nil }
method SetReadDeadline (line 193) | func (c *errConn) SetReadDeadline(time.Time) error { return nil }
method SetWriteDeadline (line 194) | func (c *errConn) SetWriteDeadline(time.Time) error { return nil }
type failingPacketConn (line 196) | type failingPacketConn struct
method ReadFrom (line 204) | func (c *failingPacketConn) ReadFrom([]byte) (int, net.Addr, error) {
method WriteTo (line 208) | func (c *failingPacketConn) WriteTo([]byte, net.Addr) (int, error) {
method Close (line 212) | func (c *failingPacketConn) Close() error { return...
method LocalAddr (line 213) | func (c *failingPacketConn) LocalAddr() net.Addr { return...
method SetDeadline (line 214) | func (c *failingPacketConn) SetDeadline(time.Time) error { return...
method SetReadDeadline (line 215) | func (c *failingPacketConn) SetReadDeadline(time.Time) error { return...
method SetWriteDeadline (line 216) | func (c *failingPacketConn) SetWriteDeadline(time.Time) error { return...
function TestDTLSTransport_Start_ErrICEConnectionNotStarted (line 218) | func TestDTLSTransport_Start_ErrICEConnectionNotStarted(t *testing.T) {
function TestDTLSTransport_Start_ConnectErrorFailsTransport (line 226) | func TestDTLSTransport_Start_ConnectErrorFailsTransport(t *testing.T) {
function TestDTLSTransport_Start_HandshakeErrorFailsTransport (line 261) | func TestDTLSTransport_Start_HandshakeErrorFailsTransport(t *testing.T) {
function TestDTLSTransport_dtlsSharedOptions_IncludesOptionalOptions (line 298) | func TestDTLSTransport_dtlsSharedOptions_IncludesOptionalOptions(t *test...
function TestDTLSTransport_toDTLSClientOptions_IncludesOptionalOptions (line 403) | func TestDTLSTransport_toDTLSClientOptions_IncludesOptionalOptions(t *te...
function TestDTLSTransport_verifyPeerCertificateFunc_NoRemoteCertificate (line 420) | func TestDTLSTransport_verifyPeerCertificateFunc_NoRemoteCertificate(t *...
function TestDTLSTransport_verifyPeerCertificateFunc_ParseError (line 429) | func TestDTLSTransport_verifyPeerCertificateFunc_ParseError(t *testing.T) {
function TestDTLSTransport_toDTLSServerOptions_IncludesOptionalOptions (line 439) | func TestDTLSTransport_toDTLSServerOptions_IncludesOptionalOptions(t *te...
function TestDTLSTransport_handshakeDTLS_DeferredCancel (line 493) | func TestDTLSTransport_handshakeDTLS_DeferredCancel(t *testing.T) {
function TestSRTPProtectionProfileFromDTLS (line 529) | func TestSRTPProtectionProfileFromDTLS(t *testing.T) {
FILE: dtlstransportstate.go
type DTLSTransportState (line 7) | type DTLSTransportState
method String (line 62) | func (t DTLSTransportState) String() string {
method MarshalText (line 80) | func (t DTLSTransportState) MarshalText() ([]byte, error) {
method UnmarshalText (line 85) | func (t *DTLSTransportState) UnmarshalText(b []byte) error {
constant DTLSTransportStateUnknown (line 11) | DTLSTransportStateUnknown DTLSTransportState = iota
constant DTLSTransportStateNew (line 15) | DTLSTransportStateNew
constant DTLSTransportStateConnecting (line 19) | DTLSTransportStateConnecting
constant DTLSTransportStateConnected (line 23) | DTLSTransportStateConnected
constant DTLSTransportStateClosed (line 28) | DTLSTransportStateClosed
constant DTLSTransportStateFailed (line 33) | DTLSTransportStateFailed
constant dtlsTransportStateNewStr (line 38) | dtlsTransportStateNewStr = "new"
constant dtlsTransportStateConnectingStr (line 39) | dtlsTransportStateConnectingStr = "connecting"
constant dtlsTransportStateConnectedStr (line 40) | dtlsTransportStateConnectedStr = "connected"
constant dtlsTransportStateClosedStr (line 41) | dtlsTransportStateClosedStr = "closed"
constant dtlsTransportStateFailedStr (line 42) | dtlsTransportStateFailedStr = "failed"
function newDTLSTransportState (line 45) | func newDTLSTransportState(raw string) DTLSTransportState {
FILE: dtlstransportstate_test.go
function TestNewDTLSTransportState (line 12) | func TestNewDTLSTransportState(t *testing.T) {
function TestDTLSTransportState_String (line 34) | func TestDTLSTransportState_String(t *testing.T) {
FILE: e2e/e2e_test.go
function TestE2E_Audio (line 43) | func TestE2E_Audio(t *testing.T) {
function TestE2E_DataChannel (line 154) | func TestE2E_DataChannel(t *testing.T) {
type stats (line 258) | type stats
function logParseLoop (line 264) | func logParseLoop(ctx context.Context, t *testing.T, page *agouti.Page, ...
function parseLog (line 318) | func parseLog(log agouti.Log) (string, string, bool) {
function createTrack (line 334) | func createTrack(offer webrtc.SessionDescription) (*webrtc.PeerConnectio...
FILE: examples/bandwidth-estimation-from-disk/main.go
constant lowFile (line 29) | lowFile = "low.ivf"
constant lowBitrate (line 30) | lowBitrate = 300_000
constant medFile (line 32) | medFile = "med.ivf"
constant medBitrate (line 33) | medBitrate = 1_000_000
constant highFile (line 35) | highFile = "high.ivf"
constant highBitrate (line 36) | highBitrate = 2_500_000
constant ivfHeaderSize (line 38) | ivfHeaderSize = 32
function main (line 41) | func main() { //nolint:gocognit,cyclop,maintidx
function setReaderFile (line 258) | func setReaderFile(filename string) func(_ int64) io.Reader {
function readUntilNewline (line 273) | func readUntilNewline() (in string) {
function encode (line 294) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 304) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/broadcast/main.go
function main (line 25) | func main() {
function encode (line 206) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 216) | func decode(in string, obj *webrtc.SessionDescription) {
function httpSDPServer (line 228) | func httpSDPServer(port int) chan string {
FILE: examples/custom-logger/main.go
type customLogger (line 22) | type customLogger struct
method Trace (line 25) | func (c customLogger) Trace(string) {}
method Tracef (line 26) | func (c customLogger) Tracef(string, ...any) {}
method Debug (line 28) | func (c customLogger) Debug(msg string) { fmt.Printf("customLogger Deb...
method Debugf (line 29) | func (c customLogger) Debugf(format string, args ...any) {
method Info (line 32) | func (c customLogger) Info(msg string) { fmt.Printf("customLogger Info...
method Infof (line 33) | func (c customLogger) Infof(format string, args ...any) {
method Warn (line 36) | func (c customLogger) Warn(msg string) { fmt.Printf("customLogger Warn...
method Warnf (line 37) | func (c customLogger) Warnf(format string, args ...any) {
method Error (line 40) | func (c customLogger) Error(msg string) { fmt.Printf("customLogger Err...
method Errorf (line 41) | func (c customLogger) Errorf(format string, args ...any) {
type customLoggerFactory (line 48) | type customLoggerFactory struct
method NewLogger (line 50) | func (c customLoggerFactory) NewLogger(subsystem string) logging.Level...
function main (line 57) | func main() {
FILE: examples/data-channels-detach-create/main.go
constant messageSize (line 24) | messageSize = 15
function main (line 26) | func main() {
function ReadLoop (line 141) | func ReadLoop(d io.Reader) {
function WriteLoop (line 156) | func WriteLoop(d io.Writer) {
function httpSDPServer (line 175) | func httpSDPServer(port int) chan string {
function encode (line 192) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 202) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/data-channels-detach/jsfiddle/main.go
constant messageSize (line 25) | messageSize = 15
function main (line 27) | func main() {
function ReadLoop (line 147) | func ReadLoop(d io.Reader) {
function WriteLoop (line 161) | func WriteLoop(d io.Writer) {
function log (line 177) | func log(msg string) {
function handleError (line 182) | func handleError(err error) {
function getElementByID (line 187) | func getElementByID(id string) js.Value {
function readUntilNewline (line 192) | func readUntilNewline() (in string) {
function encode (line 212) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 222) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/data-channels-detach/main.go
constant messageSize (line 25) | messageSize = 15
function main (line 27) | func main() {
function ReadLoop (line 142) | func ReadLoop(d io.Reader) {
function WriteLoop (line 157) | func WriteLoop(d io.Writer) {
function readUntilNewline (line 176) | func readUntilNewline() (in string) {
function encode (line 197) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 207) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/data-channels-flow-control/main.go
constant bufferedAmountLowThreshold (line 19) | bufferedAmountLowThreshold uint64 = 512 * 1024
constant maxBufferedAmount (line 20) | maxBufferedAmount uint64 = 1024 * 1024
function check (line 23) | func check(err error) {
function setRemoteDescription (line 29) | func setRemoteDescription(pc *webrtc.PeerConnection, sdp []byte) {
function createOfferer (line 39) | func createOfferer() *webrtc.PeerConnection {
function createAnswerer (line 98) | func createAnswerer() *webrtc.PeerConnection {
function main (line 135) | func main() {
FILE: examples/data-channels-simple/main.go
function main (line 17) | func main() {
function setupOfferHandler (line 31) | func setupOfferHandler(pc **webrtc.PeerConnection) {
function setupICECandidateHandler (line 66) | func setupICECandidateHandler(pc *webrtc.PeerConnection) {
function setupDataChannelHandler (line 74) | func setupDataChannelHandler(pc *webrtc.PeerConnection) {
function processOffer (line 88) | func processOffer(
function setupCandidateHandler (line 127) | func setupCandidateHandler(pc **webrtc.PeerConnection) {
function setupStaticHandler (line 143) | func setupStaticHandler() {
FILE: examples/data-channels-whip-whep-like/main.go
type Hub (line 39) | type Hub struct
method Register (line 58) | func (h *Hub) Register(channel *webrtc.DataChannel) string {
method Unregister (line 70) | func (h *Hub) Unregister(channel *webrtc.DataChannel) {
method generateUniqueUsername (line 81) | func (h *Hub) generateUniqueUsername() string {
method GetUsername (line 108) | func (h *Hub) GetUsername(channel *webrtc.DataChannel) string {
method Broadcast (line 116) | func (h *Hub) Broadcast(message string, sender *webrtc.DataChannel) {
method Count (line 140) | func (h *Hub) Count() int {
function main (line 148) | func main() {
function whipHandler (line 158) | func whipHandler(res http.ResponseWriter, req *http.Request) {
function whepHandler (line 175) | func whepHandler(res http.ResponseWriter, req *http.Request) {
function writeAnswer (line 192) | func writeAnswer(res http.ResponseWriter, peerConnection *webrtc.PeerCon...
FILE: examples/data-channels/jsfiddle/main.go
function main (line 23) | func main() {
function log (line 148) | func log(msg string) {
function handleError (line 153) | func handleError(err error) {
function getElementByID (line 158) | func getElementByID(id string) js.Value {
function readUntilNewline (line 163) | func readUntilNewline() (in string) {
function encode (line 183) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 193) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/data-channels/main.go
function main (line 23) | func main() {
function readUntilNewline (line 138) | func readUntilNewline() (in string) {
function encode (line 159) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 169) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/examples.go
type Examples (line 20) | type Examples
type Example (line 23) | type Example struct
function main (line 32) | func main() {
function serve (line 43) | func serve(addr string) error {
function getExamples (line 121) | func getExamples() *Examples {
FILE: examples/ice-proxy/answer.go
function setupAnsweringAgent (line 17) | func setupAnsweringAgent() {
FILE: examples/ice-proxy/main.go
constant turnServerAddr (line 10) | turnServerAddr = "localhost:17342"
constant turnServerURL (line 11) | turnServerURL = "turn:" + turnServerAddr + "?transport=tcp"
constant turnUsername (line 12) | turnUsername = "turn_username"
constant turnPassword (line 13) | turnPassword = "turn_password"
function main (line 16) | func main() {
FILE: examples/ice-proxy/offer.go
function setupOfferingAgent (line 19) | func setupOfferingAgent() {
FILE: examples/ice-proxy/proxy.go
type proxyDialer (line 22) | type proxyDialer struct
method Dial (line 36) | func (d *proxyDialer) Dial(network, addr string) (net.Conn, error) {
function newProxyDialer (line 26) | func newProxyDialer(u *url.URL) proxy.Dialer {
function newHTTPProxy (line 77) | func newHTTPProxy() *url.URL {
function proxyHandleConn (line 99) | func proxyHandleConn(clientConn net.Conn) {
FILE: examples/ice-proxy/turn.go
function newTURNServer (line 14) | func newTURNServer() *turn.Server {
FILE: examples/ice-restart/main.go
function doSignaling (line 19) | func doSignaling(res http.ResponseWriter, req *http.Request) {
function main (line 80) | func main() {
FILE: examples/ice-single-port/main.go
function doSignaling (line 22) | func doSignaling(res http.ResponseWriter, req *http.Request) {
function main (line 80) | func main() {
FILE: examples/ice-tcp/main.go
function doSignaling (line 23) | func doSignaling(res http.ResponseWriter, req *http.Request) { //nolint:...
function main (line 85) | func main() {
FILE: examples/insertable-streams/jsfiddle/demo.js
function getInsertableStream (line 83) | function getInsertableStream (transceiver) {
FILE: examples/insertable-streams/main.go
constant cipherKey (line 26) | cipherKey = 0xAA
function main (line 29) | func main() {
function readUntilNewline (line 184) | func readUntilNewline() (in string) {
function encode (line 205) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 215) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/ortc-media/main.go
constant videoFileName (line 29) | videoFileName = "output.ivf"
function main (line 33) | func main() {
function fourCCToTrack (line 200) | func fourCCToTrack(fourCC string) *webrtc.TrackLocalStaticSample {
function writeFileToTrack (line 224) | func writeFileToTrack(ivf *ivfreader.IVFReader, header *ivfreader.IVFFil...
type Signal (line 249) | type Signal struct
function readUntilNewline (line 257) | func readUntilNewline() (in string) {
function encode (line 278) | func encode(obj *Signal) string {
function decode (line 288) | func decode(in string, obj *Signal) {
function httpSDPServer (line 300) | func httpSDPServer(port int) chan string {
FILE: examples/ortc/main.go
function main (line 28) | func main() {
type Signal (line 175) | type Signal struct
function handleOnOpen (line 182) | func handleOnOpen(channel *webrtc.DataChannel) func() {
function readUntilNewline (line 206) | func readUntilNewline() (in string) {
function encode (line 227) | func encode(obj Signal) string {
function decode (line 237) | func decode(in string, obj *Signal) {
function httpSDPServer (line 249) | func httpSDPServer(port int) chan string {
FILE: examples/pion-to-pion/answer/main.go
function signalCandidate (line 22) | func signalCandidate(addr string, candidate *webrtc.ICECandidate) error {
function main (line 37) | func main() {
function setupDataChannel (line 183) | func setupDataChannel(dataChannel *webrtc.DataChannel) {
FILE: examples/pion-to-pion/offer/main.go
function signalCandidate (line 22) | func signalCandidate(addr string, candidate *webrtc.ICECandidate) error {
function main (line 37) | func main() {
function setupDataChannel (line 185) | func setupDataChannel(dataChannel *webrtc.DataChannel) {
FILE: examples/play-from-disk-fec/main.go
constant videoFileName (line 31) | videoFileName = "output.ivf"
constant answerFileName (line 32) | answerFileName = "answer.txt"
function main (line 35) | func main() { //nolint:gocognit,cyclop,gocyclo,maintidx
function encode (line 262) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 272) | func decode(in string, obj *webrtc.SessionDescription) {
type packetDropInterceptorFactory (line 284) | type packetDropInterceptorFactory struct
method NewInterceptor (line 286) | func (f packetDropInterceptorFactory) NewInterceptor(_ string) (interc...
type dropFilter (line 291) | type dropFilter struct
method BindLocalStream (line 299) | func (i *dropFilter) BindLocalStream(info *interceptor.StreamInfo, wri...
FILE: examples/play-from-disk-playlist-control/main.go
constant playlistFile (line 33) | playlistFile = "playlist.ogg"
constant labelAudio (line 34) | labelAudio = "audio"
constant labelTrack (line 35) | labelTrack = "pion"
type bufferedPage (line 41) | type bufferedPage struct
type oggTrack (line 47) | type oggTrack struct
function main (line 59) | func main() { //nolint:gocognit,cyclop
function handleOffer (line 127) | func handleOffer(
function stream (line 264) | func stream(
function parsePlaylist (line 308) | func parsePlaylist(path string) ([]*oggTrack, error) { //nolint:cyclop
function ensureTrack (line 399) | func ensureTrack(tracks map[uint32]*oggTrack, serial uint32, order *[]ui...
function extractMetadata (line 412) | func extractMetadata(tags *oggreader.OpusTags) (title, artist string) {
function pageDuration (line 425) | func pageDuration(header *oggreader.OggHeader, granule, last uint64) tim...
function wrapNext (line 445) | func wrapNext(current, limit int) int {
function wrapPrev (line 453) | func wrapPrev(current, limit int) int {
function normalizeIndex (line 464) | func normalizeIndex(i, limit int) int {
function sendPlaylistText (line 478) | func sendPlaylistText(dc *webrtc.DataChannel, tracks []*oggTrack, curren...
function sendNowPlayingText (line 502) | func sendNowPlayingText(dc *webrtc.DataChannel, track *oggTrack, index i...
function nowLine (line 513) | func nowLine(track *oggTrack, index int) string {
function cleanText (line 537) | func cleanText(v string) string {
FILE: examples/play-from-disk-playlist-control/web/app.js
function startSession (line 21) | async function startSession () {
function sendPrev (line 77) | function sendPrev () {
function sendNext (line 81) | function sendNext () {
function sendList (line 85) | function sendList () {
function sendCommand (line 89) | function sendCommand () {
function sendRawCommand (line 97) | function sendRawCommand (text) {
function handleMessage (line 106) | function handleMessage (data) {
function renderPlaylist (line 159) | function renderPlaylist (message) {
function renderNowPlaying (line 175) | function renderNowPlaying (track) {
function prettyDuration (line 194) | function prettyDuration (ms) {
FILE: examples/play-from-disk-renegotiation/main.go
function doSignaling (line 26) | func doSignaling(res http.ResponseWriter, req *http.Request) {
function createPeerConnection (line 63) | func createPeerConnection(res http.ResponseWriter, req *http.Request) {
function addVideo (line 73) | func addVideo(res http.ResponseWriter, req *http.Request) { //nolint:cyclop
function removeVideo (line 128) | func removeVideo(res http.ResponseWriter, req *http.Request) {
function main (line 139) | func main() {
function writeVideoToTrack (line 188) | func writeVideoToTrack(
FILE: examples/play-from-disk/main.go
constant audioFileName (line 28) | audioFileName = "output.ogg"
constant videoFileName (line 29) | videoFileName = "output.ivf"
constant oggPageDuration (line 30) | oggPageDuration = time.Millisecond * 20
function main (line 33) | func main() { //nolint:gocognit,cyclop,gocyclo,maintidx
function readUntilNewline (line 295) | func readUntilNewline() (in string) {
function encode (line 316) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 326) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/quick-switch/main.go
function nextVideo (line 35) | func nextVideo() {
function doWHIP (line 45) | func doWHIP(res http.ResponseWriter, req *http.Request) {
function playFile (line 121) | func playFile(fileIndex int32) {
function main (line 163) | func main() {
FILE: examples/reflect/main.go
function main (line 25) | func main() {
function readUntilNewline (line 186) | func readUntilNewline() (in string) {
function encode (line 207) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 217) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/repacketize/index.js
function log (line 8) | function log (value) {
function codecs (line 16) | function codecs () {
function start (line 36) | async function start () {
FILE: examples/repacketize/main.go
function main (line 29) | func main() {
function createRTCConn (line 88) | func createRTCConn() *webrtc.PeerConnection { //nolint:cyclop
FILE: examples/rtcp-processing/main.go
function main (line 22) | func main() {
function readUntilNewline (line 104) | func readUntilNewline() (in string) {
function encode (line 125) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 135) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/rtp-forwarder/main.go
type udpConn (line 26) | type udpConn struct
function main (line 32) | func main() { //nolint:gocognit,cyclop,maintidx
function readUntilNewline (line 251) | func readUntilNewline() (in string) {
function encode (line 272) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 282) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/rtp-to-webrtc/main.go
function main (line 24) | func main() {
function readUntilNewline (line 143) | func readUntilNewline() (in string) {
function encode (line 164) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 174) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/save-to-disk-av1/main.go
function saveToDisk (line 26) | func saveToDisk(writer media.Writer, track *webrtc.TrackRemote) {
function main (line 49) | func main() {
function readUntilNewline (line 182) | func readUntilNewline() (in string) {
function encode (line 203) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 213) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/save-to-disk/main.go
function saveToDisk (line 28) | func saveToDisk(writer media.Writer, track *webrtc.TrackRemote) {
function main (line 51) | func main() {
function readUntilNewline (line 210) | func readUntilNewline() (in string) {
function encode (line 231) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 241) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/simulcast/main.go
function main (line 25) | func main() {
function readUntilNewline (line 205) | func readUntilNewline() (in string) {
function encode (line 226) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 236) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/stats/main.go
constant statsInterval (line 27) | statsInterval = time.Second * 5
function main (line 30) | func main() {
function readUntilNewline (line 178) | func readUntilNewline() (in string) {
function encode (line 199) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 209) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/swap-tracks/jsfiddle/demo.js
function drawCircle (line 51) | function drawCircle (ctx, color, angle) {
FILE: examples/swap-tracks/main.go
function main (line 27) | func main() { // nolint:gocognit
function readUntilNewline (line 231) | func readUntilNewline() (in string) {
function encode (line 252) | func encode(obj *webrtc.SessionDescription) string {
function decode (line 262) | func decode(in string, obj *webrtc.SessionDescription) {
FILE: examples/trickle-ice/main.go
function websocketServer (line 19) | func websocketServer(wsConn *websocket.Conn) {
function main (line 139) | func main() {
FILE: examples/vnet/show-network-usage/main.go
function main (line 41) | func main() {
function panicIfError (line 229) | func panicIfError(err error) {
FILE: examples/warp/main.go
function main (line 17) | func main() {
function setupOfferHandler (line 38) | func setupOfferHandler(pc **webrtc.PeerConnection) {
function setupICECandidateHandler (line 68) | func setupICECandidateHandler(pc *webrtc.PeerConnection) {
function setupDataChannelHandler (line 76) | func setupDataChannelHandler(pc *webrtc.PeerConnection) {
function processOffer (line 100) | func processOffer(
function setupCandidateHandler (line 139) | func setupCandidateHandler(pc **webrtc.PeerConnection) {
function setupStaticHandler (line 155) | func setupStaticHandler() {
FILE: examples/whip-whep/main.go
function main (line 36) | func main() {
function whipHandler (line 58) | func whipHandler(res http.ResponseWriter, req *http.Request) { // nolint...
function whepHandler (line 185) | func whepHandler(res http.ResponseWriter, req *http.Request) { //nolint:...
function writeAnswer (line 291) | func writeAnswer(res http.ResponseWriter, peerConnection *webrtc.PeerCon...
FILE: gathering_complete_promise.go
function GatheringCompletePromise (line 17) | func GatheringCompletePromise(pc *PeerConnection) (gatherComplete <-chan...
FILE: gathering_complete_promise_example_test.go
function ExampleGatheringCompletePromise (line 16) | func ExampleGatheringCompletePromise() {
FILE: ice_go.go
method NewICETransport (line 11) | func (api *API) NewICETransport(gatherer *ICEGatherer) *ICETransport {
FILE: icecandidate.go
type ICECandidate (line 14) | type ICECandidate struct
method ToICE (line 85) | func (c ICECandidate) ToICE() (cand ice.Candidate, err error) {
method setExtensions (line 154) | func (c *ICECandidate) setExtensions(ext []ice.CandidateExtension) {
method exportExtensions (line 168) | func (c *ICECandidate) exportExtensions(cand ice.Candidate) error {
method String (line 220) | func (c ICECandidate) String() string {
method ToJSON (line 231) | func (c ICECandidate) ToJSON() ICECandidateInit {
function newICECandidatesFromICE (line 32) | func newICECandidatesFromICE(
function newICECandidateFromICE (line 50) | func newICECandidateFromICE(candidate ice.Candidate, sdpMid string, sdpM...
function convertTypeFromICE (line 205) | func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
FILE: icecandidate_test.go
function TestICECandidate_Convert (line 13) | func TestICECandidate_Convert(t *testing.T) {
function TestConvertTypeFromICE (line 140) | func TestConvertTypeFromICE(t *testing.T) {
function TestNewIdentifiedICECandidateFromICE (line 158) | func TestNewIdentifiedICECandidateFromICE(t *testing.T) {
function TestNewIdentifiedICECandidatesFromICE (line 177) | func TestNewIdentifiedICECandidatesFromICE(t *testing.T) {
function TestICECandidate_ToJSON (line 206) | func TestICECandidate_ToJSON(t *testing.T) {
function TestICECandidateZeroSDPid (line 223) | func TestICECandidateZeroSDPid(t *testing.T) {
function TestICECandidateString (line 230) | func TestICECandidateString(t *testing.T) {
function TestICECandidateSDPMid_ToJSON (line 254) | func TestICECandidateSDPMid_ToJSON(t *testing.T) {
function TestICECandidateExtensions_ToJSON (line 264) | func TestICECandidateExtensions_ToJSON(t *testing.T) {
FILE: icecandidateinit.go
type ICECandidateInit (line 7) | type ICECandidateInit struct
FILE: icecandidateinit_test.go
function TestICECandidateInit_Serialization (line 13) | func TestICECandidateInit_Serialization(t *testing.T) {
function refString (line 42) | func refString(s string) *string {
function refUint16 (line 46) | func refUint16(i uint16) *uint16 {
FILE: icecandidatepair.go
type ICECandidatePair (line 9) | type ICECandidatePair struct
method String (line 19) | func (p *ICECandidatePair) String() string {
function newICECandidatePairStatsID (line 15) | func newICECandidatePairStatsID(localID, remoteID string) string {
function NewICECandidatePair (line 29) | func NewICECandidatePair(local, remote *ICECandidate) *ICECandidatePair {
FILE: icecandidatepair_test.go
function TestICECandidatePairString_Nil (line 12) | func TestICECandidatePairString_Nil(t *testing.T) {
FILE: icecandidatetype.go
type ICECandidateType (line 13) | type ICECandidateType
method String (line 70) | func (t ICECandidateType) String() string {
method MarshalText (line 104) | func (t ICECandidateType) MarshalText() ([]byte, error) { //nolint:sta...
method UnmarshalText (line 109) | func (t *ICECandidateType) UnmarshalText(b []byte) error {
method toICE (line 116) | func (r ICECandidateType) toICE() ice.CandidateType {
constant ICECandidateTypeUnknown (line 17) | ICECandidateTypeUnknown ICECandidateType = iota
constant ICECandidateTypeHost (line 24) | ICECandidateTypeHost
constant ICECandidateTypeSrflx (line 32) | ICECandidateTypeSrflx
constant ICECandidateTypePrflx (line 38) | ICECandidateTypePrflx
constant ICECandidateTypeRelay (line 43) | ICECandidateTypeRelay
constant iceCandidateTypeHostStr (line 48) | iceCandidateTypeHostStr = "host"
constant iceCandidateTypeSrflxStr (line 49) | iceCandidateTypeSrflxStr = "srflx"
constant iceCandidateTypePrflxStr (line 50) | iceCandidateTypePrflxStr = "prflx"
constant iceCandidateTypeRelayStr (line 51) | iceCandidateTypeRelayStr = "relay"
function NewICECandidateType (line 55) | func NewICECandidateType(raw string) (ICECandidateType, error) {
function getCandidateType (line 85) | func getCandidateType(candidateType ice.CandidateType) (ICECandidateType...
FILE: icecandidatetype_test.go
function TestICECandidateType (line 12) | func TestICECandidateType(t *testing.T) {
function TestICECandidateType_String (line 40) | func TestICECandidateType_String(t *testing.T) {
FILE: icecomponent.go
type ICEComponent (line 8) | type ICEComponent
method String (line 44) | func (t ICEComponent) String() string {
constant ICEComponentUnknown (line 12) | ICEComponentUnknown ICEComponent = iota
constant ICEComponentRTP (line 19) | ICEComponentRTP
constant ICEComponentRTCP (line 24) | ICEComponentRTCP
constant iceComponentRTPStr (line 29) | iceComponentRTPStr = "rtp"
constant iceComponentRTCPStr (line 30) | iceComponentRTCPStr = "rtcp"
function newICEComponent (line 33) | func newICEComponent(raw string) ICEComponent {
FILE: icecomponent_test.go
function TestICEComponent (line 12) | func TestICEComponent(t *testing.T) {
function TestICEComponent_String (line 31) | func TestICEComponent_String(t *testing.T) {
FILE: iceconnectionstate.go
type ICEConnectionState (line 7) | type ICEConnectionState
method String (line 81) | func (c ICEConnectionState) String() string {
constant ICEConnectionStateUnknown (line 11) | ICEConnectionStateUnknown ICEConnectionState = iota
constant ICEConnectionStateNew (line 17) | ICEConnectionStateNew
constant ICEConnectionStateChecking (line 22) | ICEConnectionStateChecking
constant ICEConnectionStateConnected (line 27) | ICEConnectionStateConnected
constant ICEConnectionStateCompleted (line 32) | ICEConnectionStateCompleted
constant ICEConnectionStateDisconnected (line 37) | ICEConnectionStateDisconnected
constant ICEConnectionStateFailed (line 41) | ICEConnectionStateFailed
constant ICEConnectionStateClosed (line 45) | ICEConnectionStateClosed
constant iceConnectionStateNewStr (line 50) | iceConnectionStateNewStr = "new"
constant iceConnectionStateCheckingStr (line 51) | iceConnectionStateCheckingStr = "checking"
constant iceConnectionStateConnectedStr (line 52) | iceConnectionStateConnectedStr = "connected"
constant iceConnectionStateCompletedStr (line 53) | iceConnectionStateCompletedStr = "completed"
constant iceConnectionStateDisconnectedStr (line 54) | iceConnectionStateDisconnectedStr = "disconnected"
constant iceConnectionStateFailedStr (line 55) | iceConnectionStateFailedStr = "failed"
constant iceConnectionStateClosedStr (line 56) | iceConnectionStateClosedStr = "closed"
function NewICEConnectionState (line 60) | func NewICEConnectionState(raw string) ICEConnectionState {
FILE: iceconnectionstate_test.go
function TestNewICEConnectionState (line 12) | func TestNewICEConnectionState(t *testing.T) {
function TestICEConnectionState_String (line 36) | func TestICEConnectionState_String(t *testing.T) {
FILE: icecredentialtype.go
type ICECredentialType (line 13) | type ICECredentialType
method String (line 42) | func (t ICECredentialType) String() string {
method UnmarshalJSON (line 54) | func (t *ICECredentialType) UnmarshalJSON(b []byte) error {
method MarshalJSON (line 71) | func (t ICECredentialType) MarshalJSON() ([]byte, error) {
constant ICECredentialTypePassword (line 18) | ICECredentialTypePassword ICECredentialType = iota
constant ICECredentialTypeOauth (line 22) | ICECredentialTypeOauth
constant iceCredentialTypePasswordStr (line 27) | iceCredentialTypePasswordStr = "password"
constant iceCredentialTypeOauthStr (line 28) | iceCredentialTypeOauthStr = "oauth"
function newICECredentialType (line 31) | func newICECredentialType(raw string) (ICECredentialType, error) {
FILE: icecredentialtype_test.go
function TestNewICECredentialType (line 13) | func TestNewICECredentialType(t *testing.T) {
function TestICECredentialType_String (line 32) | func TestICECredentialType_String(t *testing.T) {
function TestICECredentialType_new (line 50) | func TestICECredentialType_new(t *testing.T) {
function TestICECredentialType_Json (line 69) | func TestICECredentialType_Json(t *testing.T) {
FILE: icegatherer.go
type ICEGatherer (line 24) | type ICEGatherer struct
method updateServers (line 126) | func (g *ICEGatherer) updateServers(servers []ICEServer, policy ICETra...
method validatedServersCount (line 151) | func (g *ICEGatherer) validatedServersCount() int {
method createAgent (line 158) | func (g *ICEGatherer) createAgent() error {
method buildAgentOptions (line 181) | func (g *ICEGatherer) buildAgentOptions() ([]ice.AgentOption, error) {
method resolveCandidateTypes (line 210) | func (g *ICEGatherer) resolveCandidateTypes() []ice.CandidateType {
method resolveNAT1To1CandidateType (line 226) | func (g *ICEGatherer) resolveNAT1To1CandidateType() ice.CandidateType {
method sanitizedMDNSMode (line 237) | func (g *ICEGatherer) sanitizedMDNSMode() ice.MulticastDNSMode {
method baseAgentOptions (line 246) | func (g *ICEGatherer) baseAgentOptions(mDNSMode ice.MulticastDNSMode) ...
method credentialOptions (line 263) | func (g *ICEGatherer) credentialOptions() []ice.AgentOption {
method addressRewriteOptions (line 275) | func (g *ICEGatherer) addressRewriteOptions(candidateType ice.Candidat...
method timeoutOptions (line 300) | func (g *ICEGatherer) timeoutOptions() []ice.AgentOption {
method miscOptions (line 331) | func (g *ICEGatherer) miscOptions() []ice.AgentOption {
method renominationOptions (line 353) | func (g *ICEGatherer) renominationOptions() []ice.AgentOption {
method Gather (line 408) | func (g *ICEGatherer) Gather() error { //nolint:cyclop
method setMediaStreamIdentification (line 480) | func (g *ICEGatherer) setMediaStreamIdentification(mid string, mLineIn...
method flushCandidates (line 485) | func (g *ICEGatherer) flushCandidates() {
method Close (line 526) | func (g *ICEGatherer) Close() error {
method GracefulClose (line 533) | func (g *ICEGatherer) GracefulClose() error {
method close (line 537) | func (g *ICEGatherer) close(shouldGracefullyClose bool) error {
method GetLocalParameters (line 568) | func (g *ICEGatherer) GetLocalParameters() (ICEParameters, error) {
method GetLocalCandidates (line 592) | func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) {
method OnLocalCandidate (line 620) | func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) {
method OnStateChange (line 625) | func (g *ICEGatherer) OnStateChange(f func(ICEGathererState)) {
method State (line 630) | func (g *ICEGatherer) State() ICEGathererState {
method setState (line 634) | func (g *ICEGatherer) setState(s ICEGathererState) {
method getAgent (line 642) | func (g *ICEGatherer) getAgent() *ice.Agent {
method collectStats (line 649) | func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
method getSelectedCandidatePairStats (line 730) | func (g *ICEGatherer) getSelectedCandidatePairStats() (ICECandidatePai...
type ICEAddressRewriteMode (line 54) | type ICEAddressRewriteMode
method toICE (line 62) | func (r ICEAddressRewriteMode) toICE() ice.AddressRewriteMode {
constant ICEAddressRewriteModeUnspecified (line 57) | ICEAddressRewriteModeUnspecified ICEAddressRewriteMode = iota
constant ICEAddressRewriteReplace (line 58) | ICEAddressRewriteReplace
constant ICEAddressRewriteAppend (line 59) | ICEAddressRewriteAppend
type ICEAddressRewriteRule (line 67) | type ICEAddressRewriteRule struct
method toICE (line 77) | func (r ICEAddressRewriteRule) toICE() ice.AddressRewriteRule {
method NewICEGatherer (line 98) | func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, err...
function legacyNAT1To1AddressRewriteRules (line 378) | func legacyNAT1To1AddressRewriteRules(ips []string, candidateType ice.Ca...
FILE: icegatherer_test.go
function TestNewICEGatherer_Success (line 28) | func TestNewICEGatherer_Success(t *testing.T) {
function TestICEGather_mDNSCandidateGathering (line 68) | func TestICEGather_mDNSCandidateGathering(t *testing.T) {
function TestICEGatherer_InvalidMDNSHostName (line 95) | func TestICEGatherer_InvalidMDNSHostName(t *testing.T) {
function TestICEGatherer_updateServers (line 113) | func TestICEGatherer_updateServers(t *testing.T) {
function TestLegacyNAT1To1AddressRewriteRules (line 133) | func TestLegacyNAT1To1AddressRewriteRules(t *testing.T) {
function TestLegacyNAT1To1AddressRewriteRulesVNet (line 169) | func TestLegacyNAT1To1AddressRewriteRulesVNet(t *testing.T) { //nolint:c...
function TestICEAddressRewriteRulesWithNAT1To1Conflict (line 273) | func TestICEAddressRewriteRulesWithNAT1To1Conflict(t *testing.T) {
function gatherCandidatesWithSettingEngine (line 329) | func gatherCandidatesWithSettingEngine(t *testing.T, se SettingEngine, o...
function TestICEGatherer_NoHostPolicyVNet (line 358) | func TestICEGatherer_NoHostPolicyVNet(t *testing.T) { //nolint:cyclop
function TestICEGatherer_AddressRewriteRulesVNet (line 507) | func TestICEGatherer_AddressRewriteRulesVNet(t *testing.T) { //nolint:cy...
function TestICEGatherer_AddressRewriteRuleFilters (line 596) | func TestICEGatherer_AddressRewriteRuleFilters(t *testing.T) { //nolint:...
function TestICEGatherer_AddressRewriteHostAppendAndReplace (line 698) | func TestICEGatherer_AddressRewriteHostAppendAndReplace(t *testing.T) {
function TestICEGatherer_AddressRewriteSrflxReplace (line 762) | func TestICEGatherer_AddressRewriteSrflxReplace(t *testing.T) {
function TestICEGatherer_AddressRewriteSrflxAppendWithCatchAll (line 820) | func TestICEGatherer_AddressRewriteSrflxAppendWithCatchAll(t *testing.T) {
function TestICEGatherer_AddressRewriteMultipleRulesOrdering (line 880) | func TestICEGatherer_AddressRewriteMultipleRulesOrdering(t *testing.T) {
function TestICEGatherer_AddressRewriteIfaceScope (line 942) | func TestICEGatherer_AddressRewriteIfaceScope(t *testing.T) {
function TestICEConnection_AddressRewriteAppend (line 1003) | func TestICEConnection_AddressRewriteAppend(t *testing.T) { //nolint:cyclop
function TestICEAddressRewriteDropRule (line 1082) | func TestICEAddressRewriteDropRule(t *testing.T) {
function TestICEGatherer_AddressRewriteRelayVNet (line 1104) | func TestICEGatherer_AddressRewriteRelayVNet(t *testing.T) { //nolint:cy...
function TestICEGatherer_AddressRewriteRelayAppendVNet (line 1205) | func TestICEGatherer_AddressRewriteRelayAppendVNet(t *testing.T) { //nol...
function TestICEGatherer_StaticLocalCredentialsVNet (line 1309) | func TestICEGatherer_StaticLocalCredentialsVNet(t *testing.T) { //nolint...
function TestICEGatherer_AlreadyClosed (line 1386) | func TestICEGatherer_AlreadyClosed(t *testing.T) {
function TestICEGatherer_MaxBindingRequests (line 1441) | func TestICEGatherer_MaxBindingRequests(t *testing.T) { //nolint:cyclop
function TestICEGatherer_DisableActiveTCP (line 1557) | func TestICEGatherer_DisableActiveTCP(t *testing.T) { //nolint:cyclop
function TestICEGatherer_HostAcceptanceMinWait (line 1647) | func TestICEGatherer_HostAcceptanceMinWait(t *testing.T) {
function TestICEGatherer_SrflxAcceptanceMinWait (line 1677) | func TestICEGatherer_SrflxAcceptanceMinWait(t *testing.T) { //nolint:cyclop
function TestICEGatherer_PrflxAcceptanceMinWait (line 1812) | func TestICEGatherer_PrflxAcceptanceMinWait(t *testing.T) { //nolint:cyclop
function TestICEGatherer_STUNGatherTimeout (line 1901) | func TestICEGatherer_STUNGatherTimeout(t *testing.T) {
function TestICEGatherer_RelayAcceptanceMinWait (line 1960) | func TestICEGatherer_RelayAcceptanceMinWait(t *testing.T) { //nolint:cyclop
function TestNewICEGathererSetMediaStreamIdentification (line 2069) | func TestNewICEGathererSetMediaStreamIdentification(t *testing.T) { //no...
function TestICEGatherer_RenominationOptions (line 2122) | func TestICEGatherer_RenominationOptions(t *testing.T) {
function TestICEGatherer_RenominationOptionsDisabled (line 2131) | func TestICEGatherer_RenominationOptionsDisabled(t *testing.T) {
function TestICEGatherer_RenominationSendsNomination (line 2154) | func TestICEGatherer_RenominationSendsNomination(t *testing.T) { //nolin...
function TestICEGatherer_RenominationSwitchesPair (line 2254) | func TestICEGatherer_RenominationSwitchesPair(t *testing.T) { //nolint:c...
function TestICEGatherer_GracefulCloseDuringAgentActivity (line 2402) | func TestICEGatherer_GracefulCloseDuringAgentActivity(t *testing.T) {
function buildRenominationVNetPair (line 2430) | func buildRenominationVNetPair(
function connectAndWaitForICE (line 2496) | func connectAndWaitForICE(t *testing.T, offerPC, answerPC *PeerConnectio...
function selectedCandidatePair (line 2518) | func selectedCandidatePair(t *testing.T, pc *PeerConnection) *ice.Candid...
function waitForTwoRemoteCandidates (line 2529) | func waitForTwoRemoteCandidates(t *testing.T, pc *PeerConnection) {
function findCandidateByID (line 2542) | func findCandidateByID(t *testing.T, agent *ice.Agent, id string, local ...
function findSwitchTarget (line 2564) | func findSwitchTarget(
function getAgent (line 2603) | func getAgent(t *testing.T, pc *PeerConnection) *ice.Agent {
function candidatePairSummary (line 2614) | func candidatePairSummary(t *testing.T, agent *ice.Agent) string {
function waitDataChannelOpen (line 2648) | func waitDataChannelOpen(t *testing.T, dc *DataChannel) {
function sendAndExpect (line 2667) | func sendAndExpect(t *testing.T, sender *DataChannel, recvCh chan string...
type stagedCandidateSender (line 2681) | type stagedCandidateSender struct
method addCandidate (line 2689) | func (s *stagedCandidateSender) addCandidate(cand ICECandidateInit, sr...
method flushSrflx (line 2712) | func (s *stagedCandidateSender) flushSrflx() error {
method flushHost (line 2733) | func (s *stagedCandidateSender) flushHost() error {
method errValue (line 2754) | func (s *stagedCandidateSender) errValue() error {
function makeSrflxCandidateInit (line 2761) | func makeSrflxCandidateInit(c ICECandidate) ICECandidateInit {
function buildStagedRenominationPair (line 2769) | func buildStagedRenominationPair(
function startTrickleRenomination (line 2876) | func startTrickleRenomination(
FILE: icegathererstate.go
type ICEGathererState (line 11) | type ICEGathererState
method String (line 33) | func (s ICEGathererState) String() string {
constant ICEGathererStateUnknown (line 15) | ICEGathererStateUnknown ICEGathererState = iota
constant ICEGathererStateNew (line 19) | ICEGathererStateNew
constant ICEGathererStateGathering (line 23) | ICEGathererStateGathering
constant ICEGathererStateComplete (line 26) | ICEGathererStateComplete
constant ICEGathererStateClosed (line 30) | ICEGathererStateClosed
function atomicStoreICEGathererState (line 48) | func atomicStoreICEGathererState(state *ICEGathererState, newState ICEGa...
function atomicLoadICEGathererState (line 52) | func atomicLoadICEGathererState(state *ICEGathererState) ICEGathererState {
FILE: icegathererstate_test.go
function TestICEGathererState_String (line 12) | func TestICEGathererState_String(t *testing.T) {
FILE: icegatheringstate.go
type ICEGatheringState (line 7) | type ICEGatheringState
method String (line 48) | func (t ICEGatheringState) String() string {
constant ICEGatheringStateUnknown (line 11) | ICEGatheringStateUnknown ICEGatheringState = iota
constant ICEGatheringStateNew (line 16) | ICEGatheringStateNew
constant ICEGatheringStateGathering (line 20) | ICEGatheringStateGathering
constant ICEGatheringStateComplete (line 24) | ICEGatheringStateComplete
constant iceGatheringStateNewStr (line 29) | iceGatheringStateNewStr = "new"
constant iceGatheringStateGatheringStr (line 30) | iceGatheringStateGatheringStr = "gathering"
constant iceGatheringStateCompleteStr (line 31) | iceGatheringStateCompleteStr = "complete"
function NewICEGatheringState (line 35) | func NewICEGatheringState(raw string) ICEGatheringState {
FILE: icegatheringstate_test.go
function TestNewICEGatheringState (line 12) | func TestNewICEGatheringState(t *testing.T) {
function TestICEGatheringState_String (line 32) | func TestICEGatheringState_String(t *testing.T) {
FILE: icegatheroptions.go
type ICEGatherOptions (line 7) | type ICEGatherOptions struct
FILE: icemux.go
function NewICETCPMux (line 15) | func NewICETCPMux(logger logging.LeveledLogger, listener net.Listener, r...
function NewICEUDPMux (line 25) | func NewICEUDPMux(logger logging.LeveledLogger, udpConn net.PacketConn) ...
FILE: iceparameters.go
type ICEParameters (line 8) | type ICEParameters struct
FILE: iceprotocol.go
type ICEProtocol (line 13) | type ICEProtocol
method String (line 44) | func (t ICEProtocol) String() string {
constant ICEProtocolUnknown (line 17) | ICEProtocolUnknown ICEProtocol = iota
constant ICEProtocolUDP (line 20) | ICEProtocolUDP
constant ICEProtocolTCP (line 23) | ICEProtocolTCP
constant iceProtocolUDPStr (line 28) | iceProtocolUDPStr = "udp"
constant iceProtocolTCPStr (line 29) | iceProtocolTCPStr = "tcp"
function NewICEProtocol (line 33) | func NewICEProtocol(raw string) (ICEProtocol, error) {
FILE: iceprotocol_test.go
function TestNewICEProtocol (line 12) | func TestNewICEProtocol(t *testing.T) {
function TestICEProtocol_String (line 40) | func TestICEProtocol_String(t *testing.T) {
FILE: icerole.go
type ICERole (line 8) | type ICERole
method String (line 42) | func (t ICERole) String() string {
method MarshalText (line 54) | func (t ICERole) MarshalText() ([]byte, error) {
method UnmarshalText (line 59) | func (t *ICERole) UnmarshalText(b []byte) error {
constant ICERoleUnknown (line 12) | ICERoleUnknown ICERole = iota
constant ICERoleControlling (line 18) | ICERoleControlling
constant ICERoleControlled (line 22) | ICERoleControlled
constant iceRoleControllingStr (line 27) | iceRoleControllingStr = "controlling"
constant iceRoleControlledStr (line 28) | iceRoleControlledStr = "controlled"
function newICERole (line 31) | func newICERole(raw string) ICERole {
FILE: icerole_test.go
function TestNewICERole (line 12) | func TestNewICERole(t *testing.T) {
function TestICERole_String (line 31) | func TestICERole_String(t *testing.T) {
FILE: iceserver.go
type ICEServer (line 17) | type ICEServer struct
method parseURL (line 24) | func (s ICEServer) parseURL(i int) (*stun.URI, error) {
method validate (line 28) | func (s ICEServer) validate() error {
method urls (line 34) | func (s ICEServer) urls() ([]*stun.URI, error) { //nolint:cyclop
method iceserverUnmarshalFields (line 112) | func (s *ICEServer) iceserverUnmarshalFields(fields map[string]any) er...
method UnmarshalJSON (line 161) | func (s *ICEServer) UnmarshalJSON(b []byte) error {
method MarshalJSON (line 175) | func (s ICEServer) MarshalJSON() ([]byte, error) {
function iceserverUnmarshalUrls (line 76) | func iceserverUnmarshalUrls(val any) (*[]string, error) {
function iceserverUnmarshalOauth (line 92) | func iceserverUnmarshalOauth(val any) (*OAuthCredential, error) {
FILE: iceserver_js.go
type ICEServer (line 17) | type ICEServer struct
method parseURL (line 25) | func (s ICEServer) parseURL(i int) (*ice.URL, error) {
method validate (line 29) | func (s ICEServer) validate() ([]*ice.URL, error) {
FILE: iceserver_test.go
function TestICEServer_validate (line 17) | func TestICEServer_validate(t *testing.T) {
function TestICEServerZeroValue (line 119) | func TestICEServerZeroValue(t *testing.T) {
FILE: icetransport.go
type ICETransport (line 23) | type ICETransport struct
method GetSelectedCandidatePair (line 47) | func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, ...
method GetSelectedCandidatePairStats (line 73) | func (t *ICETransport) GetSelectedCandidatePairStats() (ICECandidatePa...
method Start (line 90) | func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParamete...
method restart (line 185) | func (t *ICETransport) restart() error {
method Stop (line 205) | func (t *ICETransport) Stop() error {
method GracefulStop (line 212) | func (t *ICETransport) GracefulStop() error {
method stop (line 216) | func (t *ICETransport) stop(shouldGracefullyClose bool) error {
method OnSelectedCandidatePairChange (line 252) | func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandid...
method onSelectedCandidatePairChange (line 256) | func (t *ICETransport) onSelectedCandidatePairChange(pair *ICECandidat...
method OnConnectionStateChange (line 264) | func (t *ICETransport) OnConnectionStateChange(f func(ICETransportStat...
method onConnectionStateChange (line 268) | func (t *ICETransport) onConnectionStateChange(state ICETransportState) {
method Role (line 278) | func (t *ICETransport) Role() ICERole {
method SetRemoteCandidates (line 286) | func (t *ICETransport) SetRemoteCandidates(remoteCandidates []ICECandi...
method AddRemoteCandidate (line 314) | func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidat...
method State (line 342) | func (t *ICETransport) State() ICETransportState {
method GetLocalParameters (line 352) | func (t *ICETransport) GetLocalParameters() (ICEParameters, error) {
method GetRemoteParameters (line 362) | func (t *ICETransport) GetRemoteParameters() (ICEParameters, error) {
method setState (line 382) | func (t *ICETransport) setState(i ICETransportState) {
method newEndpoint (line 386) | func (t *ICETransport) newEndpoint(f mux.MatchFunc) *mux.Endpoint {
method ensureGatherer (line 393) | func (t *ICETransport) ensureGatherer() error {
method Stats (line 406) | func (t *ICETransport) Stats() TransportStats {
method collectStats (line 424) | func (t *ICETransport) collectStats(collector *statsReportCollector) {
method haveRemoteCredentialsChange (line 430) | func (t *ICETransport) haveRemoteCredentialsChange(newUfrag, newPwd st...
method setRemoteCredentials (line 447) | func (t *ICETransport) setRemoteCredentials(newUfrag, newPwd string) e...
function NewICETransport (line 78) | func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.Logger...
FILE: icetransport_js.go
type ICETransport (line 13) | type ICETransport struct
method JSValue (line 19) | func (t *ICETransport) JSValue() js.Value {
method GetSelectedCandidatePair (line 25) | func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, ...
FILE: icetransport_test.go
function TestICETransport_OnConnectionStateChange (line 18) | func TestICETransport_OnConnectionStateChange(t *testing.T) {
function TestICETransport_OnSelectedCandidatePairChange (line 58) | func TestICETransport_OnSelectedCandidatePairChange(t *testing.T) {
function TestICETransport_GetSelectedCandidatePair (line 92) | func TestICETransport_GetSelectedCandidatePair(t *testing.T) {
function TestICETransport_GetLocalAndRemoteParameters (line 128) | func TestICETransport_GetLocalAndRemoteParameters(t *testing.T) {
FILE: icetransportpolicy.go
type ICETransportPolicy (line 12) | type ICETransportPolicy
method String (line 48) | func (t ICETransportPolicy) String() string {
method UnmarshalJSON (line 62) | func (t *ICETransportPolicy) UnmarshalJSON(b []byte) error {
method MarshalJSON (line 73) | func (t ICETransportPolicy) MarshalJSON() ([]byte, error) {
constant ICETransportPolicyAll (line 19) | ICETransportPolicyAll ICETransportPolicy = iota
constant ICETransportPolicyRelay (line 23) | ICETransportPolicyRelay
constant ICETransportPolicyNoHost (line 26) | ICETransportPolicyNoHost
constant iceTransportPolicyRelayStr (line 31) | iceTransportPolicyRelayStr = "relay"
constant iceTransportPolicyNoHostStr (line 32) | iceTransportPolicyNoHostStr = "nohost"
constant iceTransportPolicyAllStr (line 33) | iceTransportPolicyAllStr = "all"
function NewICETransportPolicy (line 37) | func NewICETransportPolicy(raw string) ICETransportPolicy {
FILE: icetransportpolicy_test.go
function TestNewICETransportPolicy (line 12) | func TestNewICETransportPolicy(t *testing.T) {
function TestICETransportPolicy_String (line 31) | func TestICETransportPolicy_String(t *testing.T) {
FILE: icetransportstate.go
type ICETransportState (line 9) | type ICETransportState
method String (line 83) | func (c ICETransportState) String() string {
method toICE (line 125) | func (c ICETransportState) toICE() ice.ConnectionState {
method MarshalText (line 147) | func (c ICETransportState) MarshalText() ([]byte, error) {
method UnmarshalText (line 152) | func (c *ICETransportState) UnmarshalText(b []byte) error {
constant ICETransportStateUnknown (line 13) | ICETransportStateUnknown ICETransportState = iota
constant ICETransportStateNew (line 17) | ICETransportStateNew
constant ICETransportStateChecking (line 22) | ICETransportStateChecking
constant ICETransportStateConnected (line 29) | ICETransportStateConnected
constant ICETransportStateCompleted (line 34) | ICETransportStateCompleted
constant ICETransportStateFailed (line 39) | ICETransportStateFailed
constant ICETransportStateDisconnected (line 45) | ICETransportStateDisconnected
constant ICETransportStateClosed (line 49) | ICETransportStateClosed
constant iceTransportStateNewStr (line 53) | iceTransportStateNewStr = "new"
constant iceTransportStateCheckingStr (line 54) | iceTransportStateCheckingStr = "checking"
constant iceTransportStateConnectedStr (line 55) | iceTransportStateConnectedStr = "connected"
constant iceTransportStateCompletedStr (line 56) | iceTransportStateCompletedStr = "completed"
constant iceTransportStateFailedStr (line 57) | iceTransportStateFailedStr = "failed"
constant iceTransportStateDisconnectedStr (line 58) | iceTransportStateDisconnectedStr = "disconnected"
constant iceTransportStateClosedStr (line 59) | iceTransportStateClosedStr = "closed"
function newICETransportState (line 62) | func newICETransportState(raw string) ICETransportState {
function newICETransportStateFromICE (line 104) | func newICETransportStateFromICE(i ice.ConnectionState) ICETransportState {
FILE: icetransportstate_test.go
function TestICETransportState_String (line 13) | func TestICETransportState_String(t *testing.T) {
function TestICETransportState_Convert (line 37) | func TestICETransportState_Convert(t *testing.T) {
FILE: interceptor.go
function RegisterDefaultInterceptors (line 26) | func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRe...
function RegisterDefaultInterceptorsWithOptions (line 33) | func RegisterDefaultInterceptorsWithOptions(mediaEngine *MediaEngine, in...
function ConfigureStatsInterceptor (line 77) | func ConfigureStatsInterceptor(interceptorRegistry *interceptor.Registry...
function ConfigureStatsInterceptorWithOptions (line 83) | func ConfigureStatsInterceptorWithOptions(interceptorRegistry *intercept...
function lookupStats (line 97) | func lookupStats(id string) (stats.Getter, bool) {
function cleanupStats (line 108) | func cleanupStats(id string) {
function ConfigureRTCPReports (line 116) | func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) err...
function ConfigureRTCPReportsWithOptions (line 122) | func ConfigureRTCPReportsWithOptions(interceptorRegistry *interceptor.Re...
function ConfigureNack (line 142) | func ConfigureNack(mediaEngine *MediaEngine, interceptorRegistry *interc...
function ConfigureNackWithOptions (line 148) | func ConfigureNackWithOptions(mediaEngine *MediaEngine, interceptorRegis...
function ConfigureTWCCHeaderExtensionSender (line 171) | func ConfigureTWCCHeaderExtensionSender(mediaEngine *MediaEngine, interc...
function ConfigureTWCCSender (line 196) | func ConfigureTWCCSender(mediaEngine *MediaEngine, interceptorRegistry *...
function ConfigureTWCCSenderWithOptions (line 202) | func ConfigureTWCCSenderWithOptions(mediaEngine *MediaEngine, intercepto...
function ConfigureCongestionControlFeedback (line 231) | func ConfigureCongestionControlFeedback(mediaEngine *MediaEngine, interc...
function ConfigureCongestionControlFeedbackWithOptions (line 237) | func ConfigureCongestionControlFeedbackWithOptions(mediaEngine *MediaEng...
function ConfigureSimulcastExtensionHeaders (line 252) | func ConfigureSimulcastExtensionHeaders(mediaEngine *MediaEngine) error {
function ConfigureFlexFEC03 (line 275) | func ConfigureFlexFEC03(
type interceptorToTrackLocalWriter (line 306) | type interceptorToTrackLocalWriter struct
method WriteRTP (line 309) | func (i *interceptorToTrackLocalWriter) WriteRTP(header *rtp.Header, p...
method Write (line 318) | func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) {
function createStreamInfo (line 328) | func createStreamInfo(
FILE: interceptor_option.go
type interceptorOptions (line 17) | type interceptorOptions struct
type InterceptorOption (line 29) | type InterceptorOption
function WithInterceptorLoggerFactory (line 32) | func WithInterceptorLoggerFactory(loggerFactory logging.LoggerFactory) I...
function WithNackGeneratorOptions (line 39) | func WithNackGeneratorOptions(opts ...nack.GeneratorOption) InterceptorO...
function WithNackResponderOptions (line 46) | func WithNackResponderOptions(opts ...nack.ResponderOption) InterceptorO...
function WithReportReceiverOptions (line 53) | func WithReportReceiverOptions(opts ...report.ReceiverOption) Intercepto...
function WithReportSenderOptions (line 60) | func WithReportSenderOptions(opts ...report.SenderOption) InterceptorOpt...
function WithStatsInterceptorOptions (line 67) | func WithStatsInterceptorOptions(opts ...stats.Option) InterceptorOption {
function WithTWCCOptions (line 74) | func WithTWCCOptions(opts ...twcc.Option) InterceptorOption {
FILE: interceptor_test.go
function TestPeerConnection_Interceptor (line 35) | func TestPeerConnection_Interceptor(t *testing.T) {
function Test_Interceptor_BindUnbind (line 119) | func Test_Interceptor_BindUnbind(t *testing.T) { //nolint:cyclop
function Test_InterceptorRegistry_Build (line 226) | func Test_InterceptorRegistry_Build(t *testing.T) {
function TestConfigureFlexFEC03_FECParameters (line 248) | func TestConfigureFlexFEC03_FECParameters(t *testing.T) {
function Test_Interceptor_ZeroSSRC (line 316) | func Test_Interceptor_ZeroSSRC(t *testing.T) {
function TestStatsInterceptorIsAddedByDefault (line 372) | func TestStatsInterceptorIsAddedByDefault(t *testing.T) {
function TestStatsGetterCleanup (line 400) | func TestStatsGetterCleanup(t *testing.T) {
function TestInterceptorNack (line 427) | func TestInterceptorNack(t *testing.T) {
function testInterceptorNack (line 435) | func testInterceptorNack(t *testing.T, requestNack bool) { //nolint:cyclop
function TestNackTriggersSingleRTX (line 582) | func TestNackTriggersSingleRTX(t *testing.T) { //nolint:cyclop
function TestNackNotSentForRTX (line 767) | func TestNackNotSentForRTX(t *testing.T) { //nolint:cyclop
FILE: internal/fmtp/av1.go
type av1FMTP (line 6) | type av1FMTP struct
method MimeType (line 10) | func (h *av1FMTP) MimeType() string {
method Match (line 14) | func (h *av1FMTP) Match(b FMTP) bool {
method Parameter (line 38) | func (h *av1FMTP) Parameter(key string) (string, bool) {
FILE: internal/fmtp/fmtp.go
function defaultClockRate (line 11) | func defaultClockRate(mimeType string) uint32 {
function defaultChannels (line 25) | func defaultChannels(mimeType string) uint16 {
function parseParameters (line 37) | func parseParameters(line string) map[string]string {
function ClockRateEqual (line 54) | func ClockRateEqual(mimeType string, valA, valB uint32) bool {
function ChannelsEqual (line 69) | func ChannelsEqual(mimeType string, valA, valB uint16) bool {
function paramsEqual (line 92) | func paramsEqual(valA, valB map[string]string) bool {
type FMTP (line 110) | type FMTP interface
function Parse (line 123) | func Parse(mimeType string, clockRate uint32, channels uint16, line stri...
type genericFMTP (line 156) | type genericFMTP struct
method MimeType (line 163) | func (g *genericFMTP) MimeType() string {
method Match (line 169) | func (g *genericFMTP) Match(b FMTP) bool {
method Parameter (line 181) | func (g *genericFMTP) Parameter(key string) (string, bool) {
FILE: internal/fmtp/fmtp_test.go
function TestParseParameters (line 12) | func TestParseParameters(t *testing.T) {
function TestParse (line 56) | func TestParse(t *testing.T) {
function TestMatch (line 141) | func TestMatch(t *testing.T) { //nolint:maintidx
FILE: internal/fmtp/h264.go
function profileLevelIDMatches (line 10) | func profileLevelIDMatches(a, b string) bool {
type h264FMTP (line 23) | type h264FMTP struct
method MimeType (line 27) | func (h *h264FMTP) MimeType() string {
method Match (line 44) | func (h *h264FMTP) Match(b FMTP) bool {
method Parameter (line 82) | func (h *h264FMTP) Parameter(key string) (string, bool) {
FILE: internal/fmtp/vp9.go
type vp9FMTP (line 6) | type vp9FMTP struct
method MimeType (line 10) | func (h *vp9FMTP) MimeType() string {
method Match (line 14) | func (h *vp9FMTP) Match(b FMTP) bool {
method Parameter (line 38) | func (h *vp9FMTP) Parameter(key string) (string, bool) {
FILE: internal/mux/endpoint.go
type Endpoint (line 17) | type Endpoint struct
method Close (line 24) | func (e *Endpoint) Close() (err error) {
method close (line 38) | func (e *Endpoint) close() error {
method Read (line 44) | func (e *Endpoint) Read(p []byte) (int, error) {
method ReadFrom (line 50) | func (e *Endpoint) ReadFrom(p []byte) (int, net.Addr, error) {
method Write (line 57) | func (e *Endpoint) Write(p []byte) (int, error) {
method WriteTo (line 69) | func (e *Endpoint) WriteTo(p []byte, _ net.Addr) (int, error) {
method LocalAddr (line 74) | func (e *Endpoint) LocalAddr() net.Addr {
method RemoteAddr (line 79) | func (e *Endpoint) RemoteAddr() net.Addr {
method SetDeadline (line 86) | func (e *Endpoint) SetDeadline(t time.Time) error {
method SetReadDeadline (line 93) | func (e *Endpoint) SetReadDeadline(t time.Time) error {
method SetWriteDeadline (line 99) | func (e *Endpoint) SetWriteDeadline(t time.Time) error {
method SetOnClose (line 105) | func (e *Endpoint) SetOnClose(onClose func()) {
FILE: internal/mux/mux.go
constant maxBufferSize (line 20) | maxBufferSize = 1000 * 1000
constant maxPendingPackets (line 23) | maxPendingPackets = 15
type Config (line 28) | type Config struct
type Mux (line 35) | type Mux struct
method NewEndpoint (line 64) | func (m *Mux) NewEndpoint(matchFunc MatchFunc) *Endpoint {
method RemoveEndpoint (line 83) | func (m *Mux) RemoveEndpoint(e *Endpoint) {
method Close (line 90) | func (m *Mux) Close() error {
method readLoop (line 115) | func (m *Mux) readLoop() {
method dispatch (line 148) | func (m *Mux) dispatch(buf []byte) error {
method handlePendingPackets (line 201) | func (m *Mux) handlePendingPackets(endpoint *Endpoint, matchFunc Match...
function NewMux (line 49) | func NewMux(config Config) *Mux {
FILE: internal/mux/mux_test.go
constant testPipeBufferSize (line 19) | testPipeBufferSize = 8192
function TestNoEndpoints (line 21) | func TestNoEndpoints(t *testing.T) {
function TestEndpointDeadline (line 36) | func TestEndpointDeadline(t *testing.T) {
type writeDeadlineConn (line 78) | type writeDeadlineConn struct
method SetWriteDeadline (line 83) | func (w *writeDeadlineConn) SetWriteDeadline(t time.Time) error {
function TestEndpointSetWriteDeadline (line 93) | func TestEndpointSetWriteDeadline(t *testing.T) {
type writeDeadlineErrorConn (line 118) | type writeDeadlineErrorConn struct
method SetDeadline (line 123) | func (w *writeDeadlineErrorConn) SetDeadline(t time.Time) error {
function TestEndpointSetDeadlineWriteDeadlineError (line 133) | func TestEndpointSetDeadlineWriteDeadlineError(t *testing.T) {
type muxErrorConnReadResult (line 161) | type muxErrorConnReadResult struct
type muxErrorConn (line 167) | type muxErrorConn struct
method Read (line 172) | func (m *muxErrorConn) Read(b []byte) (n int, err error) {
function TestNonFatalRead (line 191) | func TestNonFatalRead(t *testing.T) {
function TestNonFatalDispatch (line 237) | func TestNonFatalDispatch(t *testing.T) {
function BenchmarkDispatch (line 260) | func BenchmarkDispatch(b *testing.B) {
function TestPendingQueue (line 286) | func TestPendingQueue(t *testing.T) {
FILE: internal/mux/muxfunc.go
type MatchFunc (line 7) | type MatchFunc
function MatchAll (line 10) | func MatchAll([]byte) bool {
function MatchRange (line 15) | func MatchRange(lower, upper byte, buf []byte) bool {
function MatchDTLS (line 40) | func MatchDTLS(b []byte) bool {
function MatchSRTPOrSRTCP (line 46) | func MatchSRTPOrSRTCP(b []byte) bool {
function isRTCP (line 50) | func isRTCP(buf []byte) bool {
function MatchSRTP (line 60) | func MatchSRTP(buf []byte) bool {
function MatchSRTCP (line 65) | func MatchSRTCP(buf []byte) bool {
FILE: internal/util/util.go
constant runesAlpha (line 15) | runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
function MathRandAlpha (line 22) | func MathRandAlpha(n int) string {
function RandUint32 (line 27) | func RandUint32() uint32 {
function FlattenErrs (line 32) | func FlattenErrs(errs []error) error {
type multiError (line 46) | type multiError
method Error (line 48) | func (me multiError) Error() string {
method Is (line 64) | func (me multiError) Is(err error) bool {
FILE: internal/util/util_test.go
function TestMathRandAlpha (line 13) | func TestMathRandAlpha(t *testing.T) {
function TestMultiError (line 18) | func TestMultiError(t *testing.T) {
FILE: js_utils.go
function awaitPromise (line 18) | func awaitPromise(promise js.Value) (js.Value, error) {
function valueToUint16Pointer (line 48) | func valueToUint16Pointer(val js.Value) *uint16 {
function valueToStringPointer (line 56) | func valueToStringPointer(val js.Value) *string {
function stringToValueOrUndefined (line 64) | func stringToValueOrUndefined(val string) js.Value {
function uint8ToValueOrUndefined (line 71) | func uint8ToValueOrUndefined(val uint8) js.Value {
function interfaceToValueOrUndefined (line 78) | func interfaceToValueOrUndefined(val any) js.Value {
function valueToStringOrZero (line 85) | func valueToStringOrZero(val js.Value) string {
function valueToUint8OrZero (line 92) | func valueToUint8OrZero(val js.Value) uint8 {
function valueToUint16OrZero (line 99) | func valueToUint16OrZero(val js.Value) uint16 {
function valueToUint32OrZero (line 106) | func valueToUint32OrZero(val js.Value) uint32 {
function valueToStrings (line 113) | func valueToStrings(val js.Value) []string {
function valueToBoolOrFalse (line 121) | func valueToBoolOrFalse(val js.Value) bool {
function valueToBoolPointer (line 129) | func valueToBoolPointer(val js.Value) *bool {
function stringPointerToValue (line 138) | func stringPointerToValue(val *string) js.Value {
function uint16PointerToValue (line 145) | func uint16PointerToValue(val *uint16) js.Value {
function boolToValueOrUndefined (line 152) | func boolToValueOrUndefined(val bool) js.Value {
function boolPointerToValue (line 160) | func boolPointerToValue(val *bool) js.Value {
function stringsToValue (line 167) | func stringsToValue(strings []string) js.Value {
function stringEnumToValueOrUndefined (line 175) | func stringEnumToValueOrUndefined(s string) js.Value {
function recoveryToError (line 183) | func recoveryToError(e any) error {
function uint8ArrayValueToBytes (line 192) | func uint8ArrayValueToBytes(val js.Value) []byte {
FILE: mediaengine.go
type mediaEngineHeaderExtension (line 22) | type mediaEngineHeaderExtension struct
type MediaEngine (line 32) | type MediaEngine struct
method setMultiCodecNegotiation (line 47) | func (m *MediaEngine) setMultiCodecNegotiation(negotiateMultiCodecs bo...
method multiCodecNegotiation (line 55) | func (m *MediaEngine) multiCodecNegotiation() bool {
method RegisterDefaultCodecs (line 64) | func (m *MediaEngine) RegisterDefaultCodecs() error {
method addCodec (line 239) | func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPC...
method RegisterCodec (line 257) | func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPC...
method RegisterHeaderExtension (line 279) | func (m *MediaEngine) RegisterHeaderExtension(
method RegisterFeedback (line 326) | func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPC...
method getHeaderExtensionID (line 357) | func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensio...
method copy (line 379) | func (m *MediaEngine) copy() *MediaEngine {
method getCodecByPayload (line 404) | func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPC...
method collectStats (line 434) | func (m *MediaEngine) collectStats(collector *statsReportCollector) {
method matchRemoteCodec (line 463) | func (m *MediaEngine) matchRemoteCodec(
method updateHeaderExtensionFromMediaSection (line 537) | func (m *MediaEngine) updateHeaderExtensionFromMediaSection(media *sdp...
method updateHeaderExtension (line 562) | func (m *MediaEngine) updateHeaderExtension(id int, extension string, ...
method pushCodecs (line 588) | func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPC...
method updateFromRemoteDescription (line 606) | func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDesc...
method getCodecsByKind (line 716) | func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecPara...
method getRTPParametersByKind (line 738) | func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directi...
method getRTPParametersByPayloadType (line 796) | func (m *MediaEngine) getRTPParametersByPayloadType(payloadType Payloa...
method isRTXEnabled (line 842) | func (m *MediaEngine) isRTXEnabled(typ RTPCodecType, directions []RTPT...
method isFECEnabled (line 852) | func (m *MediaEngine) isFECEnabled(typ RTPCodecType, directions []RTPT...
function findCodecByPayload (line 394) | func findCodecByPayload(codecs []RTPCodecParameters, payloadType Payload...
function payloaderForCodec (line 817) | func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) {
FILE: mediaengine_test.go
function TestOpusCase (line 21) | func TestOpusCase(t *testing.T) {
function TestVideoCase (line 37) | func TestVideoCase(t *testing.T) {
function TestMediaEngineRemoteDescription (line 53) | func TestMediaEngineRemoteDescription(t *testing.T) { //nolint:maintidx
function TestMediaEngineHeaderExtensionDirection (line 482) | func TestMediaEngineHeaderExtensionDirection(t *testing.T) {
function TestMediaEngineDoubleRegister (line 576) | func TestMediaEngineDoubleRegister(t *testing.T) {
function TestMediaEngineDoubleRegisterDifferentCodec (line 646) | func TestMediaEngineDoubleRegisterDifferentCodec(t *testing.T) {
function TestUpdateHeaderExtenstionToClonedMediaEngine (line 665) | func TestUpdateHeaderExtenstionToClonedMediaEngine(t *testing.T) {
function TestExtensionIdCollision (line 689) | func TestExtensionIdCollision(t *testing.T) {
function TestCaseInsensitiveMimeType (line 785) | func TestCaseInsensitiveMimeType(t *testing.T) {
function TestRTCPFeedbackHandling (line 877) | func TestRTCPFeedbackHandling(t *testing.T) {
function TestMultiCodecNegotiation (line 949) | func TestMultiCodecNegotiation(t *testing.T) {
FILE: mimetype.go
constant MimeTypeH264 (line 9) | MimeTypeH264 = "video/H264"
constant MimeTypeH265 (line 12) | MimeTypeH265 = "video/H265"
constant MimeTypeOpus (line 15) | MimeTypeOpus = "audio/opus"
constant MimeTypeVP8 (line 18) | MimeTypeVP8 = "video/VP8"
constant MimeTypeVP9 (line 21) | MimeTypeVP9 = "video/VP9"
constant MimeTypeAV1 (line 24) | MimeTypeAV1 = "video/AV1"
constant MimeTypeG722 (line 27) | MimeTypeG722 = "audio/G722"
constant MimeTypePCMU (line 30) | MimeTypePCMU = "audio/PCMU"
constant MimeTypePCMA (line 33) | MimeTypePCMA = "audio/PCMA"
constant MimeTypeRTX (line 36) | MimeTypeRTX = "video/rtx"
constant MimeTypeFlexFEC (line 39) | MimeTypeFlexFEC = "video/flexfec"
constant MimeTypeFlexFEC03 (line 42) | MimeTypeFlexFEC03 = "video/flexfec-03"
constant MimeTypeUlpFEC (line 45) | MimeTypeUlpFEC = "video/ulpfec"
FILE: networktype.go
function supportedNetworkTypes (line 12) | func supportedNetworkTypes() []NetworkType {
type NetworkType (line 22) | type NetworkType
method String (line 49) | func (t NetworkType) String() string {
method Protocol (line 65) | func (t NetworkType) Protocol() string { //nolint:staticcheck
method toICE (line 125) | func (networkType NetworkType) toICE() ice.NetworkType {
constant NetworkTypeUnknown (line 26) | NetworkTypeUnknown NetworkType = iota
constant NetworkTypeUDP4 (line 29) | NetworkTypeUDP4
constant NetworkTypeUDP6 (line 32) | NetworkTypeUDP6
constant NetworkTypeTCP4 (line 35) | NetworkTypeTCP4
constant NetworkTypeTCP6 (line 38) | NetworkTypeTCP6
constant networkTypeUDP4Str (line 43) | networkTypeUDP4Str = "udp4"
constant networkTypeUDP6Str (line 44) | networkTypeUDP6Str = "udp6"
constant networkTypeTCP4Str (line 45) | networkTypeTCP4Str = "tcp4"
constant networkTypeTCP6Str (line 46) | networkTypeTCP6Str = "tcp6"
function NewNetworkType (line 82) | func NewNetworkType(raw string) (NetworkType, error) {
function getNetworkType (line 97) | func getNetworkType(iceNetworkType ice.NetworkType) (NetworkType, error) {
function toICENetworkTypes (line 112) | func toICENetworkTypes(networkTypes []NetworkType) []ice.NetworkType {
FILE: networktype_test.go
function TestNetworkType_String (line 12) | func TestNetworkType_String(t *testing.T) {
function TestNetworkType (line 33) | func TestNetworkType(t *testing.T) {
FILE: oauthcredential.go
type OAuthCredential (line 10) | type OAuthCredential struct
FILE: offeransweroptions.go
type OfferAnswerOptions (line 8) | type OfferAnswerOptions struct
type AnswerOptions (line 20) | type AnswerOptions struct
type OfferOptions (line 26) | type OfferOptions struct
FILE: operations.go
type operation (line 13) | type operation
type operations (line 16) | type operations struct
method Enqueue (line 41) | func (o *operations) Enqueue(op operation) {
method tryEnqueue (line 50) | func (o *operations) tryEnqueue(op operation) bool {
method IsEmpty (line 69) | func (o *operations) IsEmpty() bool {
method Done (line 78) | func (o *operations) Done() {
method GracefulClose (line 94) | func (o *operations) GracefulClose() {
method pop (line 114) | func (o *operations) pop() func() {
method start (line 130) | func (o *operations) start() {
function newOperations (line 26) | func newOperations(
FILE: operations_test.go
function TestOperations_Enqueue (line 14) | func TestOperations_Enqueue(t *testing.T) {
function TestOperations_Done (line 49) | func TestOperations_Done(*testing.T) {
function TestOperations_GracefulClose (line 56) | func TestOperations_GracefulClose(t *testing.T) {
FILE: ortc_datachannel_test.go
function TestDataChannel_ORTC_SCTPTransport (line 17) | func TestDataChannel_ORTC_SCTPTransport(t *testing.T) {
function TestDataChannel_ORTCE2E (line 47) | func TestDataChannel_ORTCE2E(t *testing.T) {
FILE: ortc_media_test.go
function Test_ORTC_Media (line 18) | func Test_ORTC_Media(t *testing.T) {
FILE: ortc_test.go
type testORTCStack (line 12) | type testORTCStack struct
method setSignal (line 20) | func (s *testORTCStack) setSignal(sig *testORTCSignal, isOffer bool) e...
method getSignal (line 52) | func (s *testORTCStack) getSignal() (*testORTCSignal, error) {
method close (line 90) | func (s *testORTCStack) close() error {
type testORTCSignal (line 104) | type testORTCSignal struct
function newORTCPair (line 111) | func newORTCPair() (stackA *testORTCStack, stackB *testORTCStack, err er...
function newORTCStack (line 125) | func newORTCStack() (*testORTCStack, error) {
function signalORTCPair (line 156) | func signalORTCPair(stackA *testORTCStack, stackB *testORTCStack) error {
FILE: peerconnection.go
type PeerConnection (line 35) | type PeerConnection struct
method initConfiguration (line 218) | func (pc *PeerConnection) initConfiguration(configuration Configuratio...
method OnSignalingStateChange (line 280) | func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState...
method onSignalingStateChange (line 286) | func (pc *PeerConnection) onSignalingStateChange(newState SignalingSta...
method OnDataChannel (line 299) | func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
method OnNegotiationNeeded (line 307) | func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
method onNegotiationNeeded (line 314) | func (pc *PeerConnection) onNegotiationNeeded() {
method negotiationNeededOp (line 326) | func (pc *PeerConnection) negotiationNeededOp() {
method checkNegotiationNeeded (line 369) | func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:goc...
method OnICECandidate (line 461) | func (pc *PeerConnection) OnICECandidate(f func(*ICECandidate)) {
method OnICEGatheringStateChange (line 467) | func (pc *PeerConnection) OnICEGatheringStateChange(f func(ICEGatherin...
method OnTrack (line 483) | func (pc *PeerConnection) OnTrack(f func(*TrackRemote, *RTPReceiver)) {
method onTrack (line 489) | func (pc *PeerConnection) onTrack(t *TrackRemote, r *RTPReceiver) {
method OnICEConnectionStateChange (line 506) | func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnect...
method onICEConnectionStateChange (line 510) | func (pc *PeerConnection) onICEConnectionStateChange(cs ICEConnectionS...
method OnConnectionStateChange (line 520) | func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectio...
method onConnectionStateChange (line 524) | func (pc *PeerConnection) onConnectionStateChange(cs PeerConnectionSta...
method SetConfiguration (line 534) | func (pc *PeerConnection) SetConfiguration(configuration Configuration...
method GetConfiguration (line 636) | func (pc *PeerConnection) GetConfiguration() Configuration {
method ID (line 640) | func (pc *PeerConnection) ID() string {
method hasLocalDescriptionChanged (line 649) | func (pc *PeerConnection) hasLocalDescriptionChanged(desc *SessionDesc...
method CreateOffer (line 668) | func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionD...
method createICEGatherer (line 798) | func (pc *PeerConnection) createICEGatherer() (*ICEGatherer, error) {
method updateConnectionState (line 815) | func (pc *PeerConnection) updateConnectionState(
method createICETransport (line 861) | func (pc *PeerConnection) createICETransport() *ICETransport {
method CreateAnswer (line 895) | func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (Sessio...
method setDescription (line 969) | func (pc *PeerConnection) setDescription(sd *SessionDescription, op st...
method SetLocalDescription (line 1089) | func (pc *PeerConnection) SetLocalDescription(desc SessionDescription)...
method LocalDescription (line 1151) | func (pc *PeerConnection) LocalDescription() *SessionDescription {
method SetRemoteDescription (line 1162) | func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription...
method configureReceiver (line 1370) | func (pc *PeerConnection) configureReceiver(incoming trackDetails, rec...
method startReceiver (line 1383) | func (pc *PeerConnection) startReceiver(incoming trackDetails, receive...
method configureRTPReceivers (line 1502) | func (pc *PeerConnection) configureRTPReceivers(
method startRTPReceivers (line 1593) | func (pc *PeerConnection) startRTPReceivers(remoteDesc *SessionDescrip...
method startRTPSenders (line 1636) | func (pc *PeerConnection) startRTPSenders(currentTransceivers []*RTPTr...
method startSCTP (line 1650) | func (pc *PeerConnection) startSCTP(maxMessageSize uint32) {
method handleUndeclaredSSRC (line 1664) | func (pc *PeerConnection) handleUndeclaredSSRC(
method findMediaSectionByPayloadType (line 1720) | func (pc *PeerConnection) findMediaSectionByPayloadType(
method handleNonMediaBandwidthProbe (line 1751) | func (pc *PeerConnection) handleNonMediaBandwidthProbe() {
method handleIncomingSSRC (line 1778) | func (pc *PeerConnection) handleIncomingSSRC(rtpStream *srtp.ReadStrea...
method undeclaredMediaProcessor (line 1957) | func (pc *PeerConnection) undeclaredMediaProcessor() {
method undeclaredRTPMediaProcessor (line 1962) | func (pc *PeerConnection) undeclaredRTPMediaProcessor() { //nolint:cyclop
method undeclaredRTCPMediaProcessor (line 2029) | func (pc *PeerConnection) undeclaredRTCPMediaProcessor() {
method RemoteDescription (line 2059) | func (pc *PeerConnection) RemoteDescription() *SessionDescription {
method AddICECandidate (line 2072) | func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) ...
method descriptionContainsUfrag (line 2118) | func (pc *PeerConnection) descriptionContainsUfrag(sdp *sdp.SessionDes...
method ICEConnectionState (line 2136) | func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
method GetSenders (line 2145) | func (pc *PeerConnection) GetSenders() (result []*RTPSender) {
method GetReceivers (line 2159) | func (pc *PeerConnection) GetReceivers() (receivers []*RTPReceiver) {
method GetTransceivers (line 2173) | func (pc *PeerConnection) GetTransceivers() []*RTPTransceiver {
method AddTrack (line 2183) | func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, erro...
method RemoveTrack (line 2221) | func (pc *PeerConnection) RemoveTrack(sender *RTPSender) (err error) {
method newTransceiverFromTrack (line 2249) | func (pc *PeerConnection) newTransceiverFromTrack(
method AddTransceiverFromKind (line 2286) | func (pc *PeerConnection) AddTransceiverFromKind(
method AddTransceiverFromTrack (line 2331) | func (pc *PeerConnection) AddTransceiverFromTrack(
method CreateDataChannel (line 2361) | func (pc *PeerConnection) CreateDataChannel(label string, options *Dat...
method SetIdentityProvider (line 2444) | func (pc *PeerConnection) SetIdentityProvider(string) error {
method WriteRTCP (line 2450) | func (pc *PeerConnection) WriteRTCP(pkts []rtcp.Packet) error {
method writeRTCP (line 2456) | func (pc *PeerConnection) writeRTCP(pkts []rtcp.Packet, _ interceptor....
method Close (line 2461) | func (pc *PeerConnection) Close() error {
method GracefulClose (line 2468) | func (pc *PeerConnection) GracefulClose() error {
method close (line 2472) | func (pc *PeerConnection) close(shouldGracefullyClose bool) error { //...
method addRTPTransceiver (line 2600) | func (pc *PeerConnection) addRTPTransceiver(t *RTPTransceiver) {
method CurrentLocalDescription (line 2609) | func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
method PendingLocalDescription (line 2624) | func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
method CurrentRemoteDescription (line 2639) | func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescripti...
method PendingRemoteDescription (line 2651) | func (pc *PeerConnection) PendingRemoteDescription() *SessionDescripti...
method CanTrickleICECandidates (line 2660) | func (pc *PeerConnection) CanTrickleICECandidates() ICETrickleCapabili...
method SignalingState (line 2669) | func (pc *PeerConnection) SignalingState() SignalingState {
method ICEGatheringState (line 2675) | func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
method ConnectionState (line 2692) | func (pc *PeerConnection) ConnectionState() PeerConnectionState {
method GetStats (line 2701) | func (pc *PeerConnection) GetStats() StatsReport {
method startTransports (line 2767) | func (pc *PeerConnection) startTransports(
method startRTP (line 2815) | func (pc *PeerConnection) startRTP(
method generateUnmatchedSDP (line 2834) | func (pc *PeerConnection) generateUnmatchedSDP(
method generateMatchedSDP (line 2927) | func (pc *PeerConnection) generateMatchedSDP(
method setGatherCompleteHandler (line 3096) | func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
method SCTP (line 3104) | func (pc *PeerConnection) SCTP() *SCTPTransport {
function NewPeerConnection (line 104) | func NewPeerConnection(configuration Configuration) (*PeerConnection, er...
method NewPeerConnection (line 115) | func (api *API) NewPeerConnection(configuration Configuration) (*PeerCon...
function setRTPTransceiverCurrentDirection (line 1421) | func setRTPTransceiverCurrentDirection(
function runIfNewReceiver (line 1473) | func runIfNewReceiver(
FILE: peerconnection_close_test.go
function TestPeerConnection_Close (line 18) | func TestPeerConnection_Close(t *testing.T) {
function TestPeerConnection_Close_PreICE (line 59) | func TestPeerConnection_Close_PreICE(t *testing.T) {
function TestPeerConnection_Close_DuringICE (line 92) | func TestPeerConnection_Close_DuringICE(t *testing.T) { //nolint:cyclop
function TestPeerConnection_Close_DuringICEGathering (line 147) | func TestPeerConnection_Close_DuringICEGathering(t *testing.T) { //nolin...
function TestPeerConnection_GracefulCloseWithIncomingMessages (line 177) | func TestPeerConnection_GracefulCloseWithIncomingMessages(t *testing.T) {
function TestPeerConnection_GracefulCloseWhileOpening (line 230) | func TestPeerConnection_GracefulCloseWhileOpening(t *testing.T) {
function TestPeerConnection_GracefulCloseConcurrent (line 258) | func TestPeerConnection_GracefulCloseConcurrent(t *testing.T) {
FILE: peerconnection_go_test.go
method newPair (line 42) | func (api *API) newPair(cfg Configuration) (pcOffer *PeerConnection, pcA...
function TestNew_Go (line 56) | func TestNew_Go(t *testing.T) {
function TestPeerConnection_SetConfiguration_Go (line 170) | func TestPeerConnection_SetConfiguration_Go(t *testing.T) {
function TestPeerConnection_GetConfiguration_Go (line 300) | func TestPeerConnection_GetConfiguration_Go(t *testing.T) {
function TestPeerConnection_EventHandlers_Go (line 310) | func TestPeerConnection_EventHandlers_Go(t *testing.T) {
function TestPeerConnection_ShutdownNoDTLS (line 366) | func TestPeerConnection_ShutdownNoDTLS(t *testing.T) {
function TestPeerConnection_PropertyGetters (line 403) | func TestPeerConnection_PropertyGetters(t *testing.T) {
function TestPeerConnection_AnswerWithoutOffer (line 423) | func TestPeerConnection_AnswerWithoutOffer(t *testing.T) {
function TestPeerConnection_AnswerWithClosedConnection (line 435) | func TestPeerConnection_AnswerWithClosedConnection(t *testing.T) {
function TestPeerConnection_satisfyTypeAndDirection (line 467) | func TestPeerConnection_satisfyTypeAndDirection(t *testing.T) {
function TestOneAttrKeyConnectionSetupPerMediaDescriptionInSDP (line 548) | func TestOneAttrKeyConnectionSetupPerMediaDescriptionInSDP(t *testing.T) {
function TestPeerConnection_IceLite (line 575) | func TestPeerConnection_IceLite(t *testing.T) {
function TestOnICEGatheringStateChange (line 616) | func TestOnICEGatheringStateChange(t *testing.T) {
function TestPeerConnectionTrickle (line 658) | func TestPeerConnectionTrickle(t *testing.T) { //nolint:cyclop
function TestPopulateLocalCandidates (line 753) | func TestPopulateLocalCandidates(t *testing.T) {
function TestMulticastDNSCandidates (line 793) | func TestMulticastDNSCandidates(t *testing.T) {
function TestMulticastDNSHostNameConnection (line 817) | func TestMulticastDNSHostNameConnection(t *testing.T) {
function TestICERestart (line 881) | func TestICERestart(t *testing.T) {
function TestICERestart_Error_Handling (line 961) | func TestICERestart_Error_Handling(t *testing.T) {
function TestPeerConnection_ICERestart_SetConfiguration_NewServers (line 1064) | func TestPeerConnection_ICERestart_SetConfiguration_NewServers(t *testin...
type trackRecords (line 1198) | type trackRecords struct
method newTrack (line 1204) | func (r *trackRecords) newTrack() (*TrackLocalStaticRTP, error) {
method handleTrack (line 1212) | func (r *trackRecords) handleTrack(t *TrackRemote, _ *RTPReceiver) {
method remains (line 1221) | func (r *trackRecords) remains() int {
function TestPeerConnection_MassiveTracks (line 1230) | func TestPeerConnection_MassiveTracks(t *testing.T) { //nolint:cyclop
function TestEmptyCandidate (line 1315) | func TestEmptyCandidate(t *testing.T) {
constant liteOffer (line 1348) | liteOffer = `v=0
function TestICELite (line 1364) | func TestICELite(t *testing.T) {
function TestPeerConnection_TransceiverDirection (line 1383) | func TestPeerConnection_TransceiverDirection(t *testing.T) {
function TestPeerConnection_MediaDirectionInSDP (line 1507) | func TestPeerConnection_MediaDirectionInSDP(t *testing.T) {
function TestPeerConnectionNilCallback (line 1647) | func TestPeerConnectionNilCallback(t *testing.T) {
function TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote (line 1684) | func TestTransceiverCreatedByRemoteSdpHasSameCodecOrderAsRemote(t *testi...
function TestInvalidCandidateTransport (line 1856) | func TestInvalidCandidateTransport(t *testing.T) {
function TestOfferWithInactiveDirection (line 1882) | func TestOfferWithInactiveDirection(t *testing.T) {
function TestPeerConnectionState (line 1913) | func TestPeerConnectionState(t *testing.T) {
function TestPeerConnectionDeadlock (line 1949) | func TestPeerConnectionDeadlock(t *testing.T) {
function TestPeerConnectionNoNULLCipherDefault (line 1985) | func TestPeerConnectionNoNULLCipherDefault(t *testing.T) {
function TestICETricklingSupported (line 2009) | func TestICETricklingSupported(t *testing.T) {
function TestICERenominationAdvertised (line 2061) | func TestICERenominationAdvertised(t *testing.T) {
function TestPeerConnectionTrickleMediaStreamIdentification (line 2118) | func TestPeerConnectionTrickleMediaStreamIdentification(t *testing.T) {
function TestTranceiverMediaStreamIdentification (line 2224) | func TestTranceiverMediaStreamIdentification(t *testing.T) {
function Test_WriteRTCP_Disconnected (line 2308) | func Test_WriteRTCP_Disconnected(t *testing.T) {
function Test_IPv6 (line 2319) | func Test_IPv6(t *testing.T) { //nolint: cyclop
type testICELogger (line 2394) | type testICELogger struct
method Trace (line 2398) | func (t *testICELogger) Trace(string) {}
method Tracef (line 2399) | func (t *testICELogger) Tracef(string, ...any) {}
method Debug (line 2400) | func (t *testICELogger) Debug(string) {}
method Debugf (line 2401) | func (t *testICELogger) Debugf(string, ...any) {}
method Info (line 2402) | func (t *testICELogger) Info(string) {}
method Infof (line 2403) | func (t *testICELogger) Infof(string, ...any) {}
method Warn (line 2404) | func (t *testICELogger) Warn(string) {}
method Warnf (line 2405) | func (t *testICELogger) Warnf(string, ...any) {}
method Error (line 2406) | func (t *testICELogger) Error(msg string) { t.lastErrorMessage = ...
method Errorf (line 2407) | func (t *testICELogger) Errorf(format string, args ...any) {
type testICELoggerFactory (line 2411) | type testICELoggerFactory struct
method NewLogger (line 2415) | func (t *testICELoggerFactory) NewLogger(string) logging.LeveledLogger {
function TestAddICECandidate__DroppingOldGenerationCandidates (line 2419) | func TestAddICECandidate__DroppingOldGenerationCandidates(t *testing.T) {
function TestPeerConnectionCanTrickleICECandidatesGo (line 2482) | func TestPeerConnectionCanTrickleICECandidatesGo(t *testing.T) {
function TestCreateAnswerActiveOfferPassiveAnswer (line 2519) | func TestCreateAnswerActiveOfferPassiveAnswer(t *testing.T) {
function TestCreateAnswerPassiveOfferActiveAnswer (line 2532) | func TestCreateAnswerPassiveOfferActiveAnswer(t *testing.T) {
function TestAlwaysNegotiateDataChannel_InitialOffer_Go (line 2545) | func TestAlwaysNegotiateDataChannel_InitialOffer_Go(t *testing.T) {
function TestAlwaysNegotiateDataChannels_CreateDataChannel (line 2563) | func TestAlwaysNegotiateDataChannels_CreateDataChannel(t *testing.T) { /...
function TestNoDuplicatedAttributesInMediaDescriptions (line 2672) | func TestNoDuplicatedAttributesInMediaDescriptions(t *testing.T) { //nol...
FILE: peerconnection_js.go
type PeerConnection (line 20) | type PeerConnection struct
method JSValue (line 63) | func (pc *PeerConnection) JSValue() js.Value {
method OnSignalingStateChange (line 69) | func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState...
method OnDataChannel (line 85) | func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
method OnNegotiationNeeded (line 110) | func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
method OnICEConnectionStateChange (line 125) | func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnect...
method OnConnectionStateChange (line 141) | func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectio...
method checkConfiguration (line 155) | func (pc *PeerConnection) checkConfiguration(configuration Configurati...
method SetConfiguration (line 219) | func (pc *PeerConnection) SetConfiguration(configuration Configuration...
method GetConfiguration (line 238) | func (pc *PeerConnection) GetConfiguration() Configuration {
method CreateOffer (line 243) | func (pc *PeerConnection) CreateOffer(options *OfferOptions) (_ Sessio...
method CreateAnswer (line 258) | func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (_ Sess...
method SetLocalDescription (line 273) | func (pc *PeerConnection) SetLocalDescription(desc SessionDescription)...
method LocalDescription (line 288) | func (pc *PeerConnection) LocalDescription() *SessionDescription {
method SetRemoteDescription (line 293) | func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription...
method RemoteDescription (line 308) | func (pc *PeerConnection) RemoteDescription() *SessionDescription {
method AddICECandidate (line 314) | func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) ...
method ICEConnectionState (line 327) | func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
method OnICECandidate (line 333) | func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidat...
method OnICEGatheringStateChange (line 353) | func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
method CreateDataChannel (line 369) | func (pc *PeerConnection) CreateDataChannel(label string, options *Dat...
method SetIdentityProvider (line 383) | func (pc *PeerConnection) SetIdentityProvider(provider string) (err er...
method Close (line 394) | func (pc *PeerConnection) Close() (err error) {
method CurrentLocalDescription (line 433) | func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
method PendingLocalDescription (line 442) | func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
method CurrentRemoteDescription (line 451) | func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescripti...
method PendingRemoteDescription (line 461) | func (pc *PeerConnection) PendingRemoteDescription() *SessionDescripti...
method CanTrickleICECandidates (line 468) | func (pc *PeerConnection) CanTrickleICECandidates() ICETrickleCapabili...
method SignalingState (line 482) | func (pc *PeerConnection) SignalingState() SignalingState {
method ICEGatheringState (line 489) | func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
method ConnectionState (line 496) | func (pc *PeerConnection) ConnectionState() PeerConnectionState {
method setGatherCompleteHandler (line 501) | func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
method AddTransceiverFromKind (line 512) | func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, in...
method GetTransceivers (line 531) | func (pc *PeerConnection) GetTransceivers() (transceivers []*RTPTransc...
method SCTP (line 548) | func (pc *PeerConnection) SCTP() *SCTPTransport {
function NewPeerConnection (line 42) | func NewPeerConnection(configuration Configuration) (*PeerConnection, er...
method NewPeerConnection (line 48) | func (api *API) NewPeerConnection(configuration Configuration) (_ *PeerC...
function configurationToValue (line 562) | func configurationToValue(configuration Configuration) js.Value {
function iceServersToValue (line 577) | func iceServersToValue(iceServers []ICEServer) js.Value {
function oauthCredentialToValue (line 588) | func oauthCredentialToValue(o OAuthCredential) js.Value {
function iceServerToValue (line 596) | func iceServerToValue(server ICEServer) js.Value {
function valueToConfiguration (line 615) | func valueToConfiguration(configValue js.Value) Configuration {
function valueToICEServers (line 633) | func valueToICEServers(iceServersValue js.Value) []ICEServer {
function valueToICECredential (line 644) | func valueToICECredential(iceCredentialValue js.Value) any {
function valueToICEServer (line 660) | func valueToICEServer(iceServerValue js.Value) ICEServer {
function valueToICECandidate (line 676) | func valueToICECandidate(val js.Value) *ICECandidate {
function stringToComponentIDOrZero (line 709) | func stringToComponentIDOrZero(val string) uint16 {
function sessionDescriptionToValue (line 720) | func sessionDescriptionToValue(desc *SessionDescription) js.Value {
function valueToSessionDescription (line 730) | func valueToSessionDescription(descValue js.Value) *SessionDescription {
function offerOptionsToValue (line 741) | func offerOptionsToValue(offerOptions *OfferOptions) js.Value {
function answerOptionsToValue (line 751) | func answerOptionsToValue(answerOptions *AnswerOptions) js.Value {
function iceCandidateInitToValue (line 760) | func iceCandidateInitToValue(candidate ICECandidateInit) js.Value {
function dataChannelInitToValue (line 769) | func dataChannelInitToValue(options *DataChannelInit) js.Value {
function rtpTransceiverInitInitToValue (line 788) | func rtpTransceiverInitInitToValue(init RTPTransceiverInit) js.Value {
FILE: peerconnection_js_test.go
function TestValueToICECandidate (line 14) | func TestValueToICECandidate(t *testing.T) {
function TestValueToICEServer (line 76) | func TestValueToICEServer(t *testing.T) {
function TestPeerConnectionCanTrickleICECandidatesJS (line 108) | func TestPeerConnectionCanTrickleICECandidatesJS(t *testing.T) {
function TestDTLSTransportGetRemoteCertificateMock (line 125) | func TestDTLSTransportGetRemoteCertificateMock(t *testing.T) {
FILE: peerconnection_media_test.go
function TestPeerConnection_Media_Sample (line 49) | func TestPeerConnection_Media_Sample(t *testing.T) {
function TestPeerConnection_Media_Shutdown (line 232) | func TestPeerConnection_Media_Shutdown(t *testing.T) { //nolint:cyclop
function TestPeerConnection_Media_Disconnected (line 315) | func TestPeerConnection_Media_Disconnected(t *testing.T) { //nolint:cyclop
type undeclaredSsrcLogger (line 380) | type undeclaredSsrcLogger struct
method Trace (line 382) | func (u *undeclaredSsrcLogger) Trace(string) {}
method Tracef (line 383) | func (u *undeclaredSsrcLogger) Tracef(string, ...any) {}
method Debug (line 384) | func (u *undeclaredSsrcLogger) Debug(string) {}
method Debugf (line 385) | func (u *undeclaredSsrcLogger) Debugf(string, ...any) {}
method Info (line 386) | func (u *undeclaredSsrcLogger) Info(string) {}
method Infof (line 387) | func (u *undeclaredSsrcLogger) Infof(string, ...any) {}
method Warn (line 388) | func (u *undeclaredSsrcLogger) Warn(string) {}
method Warnf (line 389) | func (u *undeclaredSsrcLogger) Warnf(string, ...any) {}
method Error (line 390) | func (u *undeclaredSsrcLogger) Error(string) {}
method Errorf (line 391) | func (u *undeclaredSsrcLogger) Errorf(format string, _ ...any) {
type undeclaredSsrcLoggerFactory (line 397) | type undeclaredSsrcLoggerFactory struct
method NewLogger (line 399) | func (u *undeclaredSsrcLoggerFactory) NewLogger(string) logging.Levele...
function filterSsrc (line 404) | func filterSsrc(offer string) (filteredSDP string) {
function filterSDPExtensions (line 418) | func filterSDPExtensions(offer string) (filteredSDP string) {
function TestUndeclaredSSRC (line 434) | func TestUndeclaredSSRC(t *testing.T) {
function TestAddTransceiverFromTrackSendOnly (line 644) | func TestAddTransceiverFromTrackSendOnly(t *testing.T) {
function TestAddTransceiverFromTrackSendRecv (line 682) | func TestAddTransceiverFromTrackSendRecv(t *testing.T) {
function TestAddTransceiverAddTrack_Reuse (line 717) | func TestAddTransceiverAddTrack_Reuse(t *testing.T) {
function TestAddTransceiverFromRemoteDescription (line 893) | func TestAddTransceiverFromRemoteDescription(t *testing.T) {
function TestAddTransceiverAddTrack_NewRTPSender_Error (line 1071) | func TestAddTransceiverAddTrack_NewRTPSender_Error(t *testing.T) {
function TestRtpSenderReceiver_ReadClose_Error (line 1096) | func TestRtpSenderReceiver_ReadClose_Error(t *testing.T) {
function TestAddTransceiverFromKind (line 1119) | func TestAddTransceiverFromKind(t *testing.T) {
function TestAddTransceiverFromTrackFailsRecvOnly (line 1147) | func TestAddTransceiverFromTrackFailsRecvOnly(t *testing.T) {
function TestPlanBMediaExchange (line 1180) | func TestPlanBMediaExchange(t *testing.T) {
function TestPeerConnection_Start_Only_Negotiated_Senders (line 1259) | func TestPeerConnection_Start_Only_Negotiated_Senders(t *testing.T) {
function TestPeerConnection_Start_Right_Receiver (line 1315) | func TestPeerConnection_Start_Right_Receiver(t *testing.T) {
function TestPeerConnection_Simulcast_Probe (line 1400) | func TestPeerConnection_Simulcast_Probe(t *testing.T) {
function TestPeerConnection_CreateOffer_NoCodecs (line 1673) | func TestPeerConnection_CreateOffer_NoCodecs(t *testing.T) {
function TestPeerConnection_RaceReplaceTrack (line 1698) | func TestPeerConnection_RaceReplaceTrack(t *testing.T) {
function TestPeerConnection_Simulcast (line 1745) | func TestPeerConnection_Simulcast(t *testing.T) { //nolint:cyclop
type simulcastTestTrackLocal (line 1943) | type simulcastTestTrackLocal struct
method WriteRTP (line 1948) | func (s *simulcastTestTrackLocal) WriteRTP(pkt *rtp.Packet) error {
function TestPeerConnection_Simulcast_RTX (line 1969) | func TestPeerConnection_Simulcast_RTX(t *testing.T) { //nolint:cyclop
function TestPeerConnection_Simulcast_LateRIDRSIDAfterReceiverClosed (line 2166) | func TestPeerConnection_Simulcast_LateRIDRSIDAfterReceiverClosed(t *test...
function TestPeerConnection_Simulcast_NoDataChannel (line 2334) | func TestPeerConnection_Simulcast_NoDataChannel(t *testing.T) {
function TestPeerConnection_Zero_PayloadType (line 2459) | func TestPeerConnection_Zero_PayloadType(t *testing.T) {
function Test_PeerConnection_RTX_E2E (line 2508) | func Test_PeerConnection_RTX_E2E(t *testing.T) { //nolint:cyclop
function TestPeerConnection_Simulcast_Probe_PacketLoss (line 2655) | func TestPeerConnection_Simulcast_Probe_PacketLoss(t *testing.T) { //nol...
FILE: peerconnection_renegotiation_test.go
function sendVideoUntilDone (line 29) | func sendVideoUntilDone(t *testing.T, done <-chan struct{}, tracks []*Tr...
function sdpMidHasSsrc (line 44) | func sdpMidHasSsrc(offer SessionDescription, mid string, ssrc SSRC) bool {
function TestPeerConnection_Renegotiation_AddRecvonlyTransceiver (line 72) | func TestPeerConnection_Renegotiation_AddRecvonlyTransceiver(t *testing....
function TestPeerConnection_Renegotiation_AddTrack (line 153) | func TestPeerConnection_Renegotiation_AddTrack(t *testing.T) {
function TestPeerConnection_Renegotiation_AddTrack_Multiple (line 216) | func TestPeerConnection_Renegotiation_AddTrack_Multiple(t *testing.T) {
function TestPeerConnection_Renegotiation_AddTrack_Rename (line 274) | func TestPeerConnection_Renegotiation_AddTrack_Rename(t *testing.T) {
function TestPeerConnection_Transceiver_Mid (line 324) | func TestPeerConnection_Transceiver_Mid(t *testing.T) {
function TestPeerConnection_Renegotiation_CodecChange (line 446) | func TestPeerConnection_Renegotiation_CodecChange(t *testing.T) {
function TestPeerConnection_Renegotiation_RemoveTrack (line 541) | func TestPeerConnection_Renegotiation_RemoveTrack(t *testing.T) {
function TestPeerConnection_RoleSwitch (line 588) | func TestPeerConnection_RoleSwitch(t *testing.T) {
function TestPeerConnection_Renegotiation_Trickle (line 629) | func TestPeerConnection_Renegotiation_Trickle(t *testing.T) {
function TestPeerConnection_Renegotiation_SetLocalDescription (line 693) | func TestPeerConnection_Renegotiation_SetLocalDescription(t *testing.T) {
function TestPeerConnection_Renegotiation_NoApplication (line 751) | func TestPeerConnection_Renegotiation_NoApplication(t *testing.T) {
function TestAddDataChannelDuringRenegotiation (line 823) | func TestAddDataChannelDuringRenegotiation(t *testing.T) {
function TestNegotiationCreateDataChannel (line 887) | func TestNegotiationCreateDataChannel(t *testing.T) {
function TestNegotiationNeededRemoveTrack (line 915) | func TestNegotiationNeededRemoveTrack(t *testing.T) {
function TestNegotiationNeededStressOneSided (line 968) | func TestNegotiationNeededStressOneSided(t *testing.T) {
function TestPeerConnection_Renegotiation_DisableTrack (line 1002) | func TestPeerConnection_Renegotiation_DisableTrack(t *testing.T) {
function TestPeerConnection_Renegotiation_Simulcast (line 1044) | func TestPeerConnection_Renegotiation_Simulcast(t *testing.T) {
function TestPeerConnection_Regegotiation_ReuseTransceiver (line 1188) | func TestPeerConnection_Regegotiation_ReuseTransceiver(t *testing.T) {
function TestPeerConnection_Renegotiation_MidConflict (line 1269) | func TestPeerConnection_Renegotiation_MidConflict(t *testing.T) {
function TestPeerConnection_Regegotiation_AnswerAddsTrack (line 1322) | func TestPeerConnection_Regegotiation_AnswerAddsTrack(t *testing.T) {
function TestNegotiationNeededWithRecvonlyTrack (line 1372) | func TestNegotiationNeededWithRecvonlyTrack(t *testing.T) {
function TestNegotiationNotNeededAfterReplaceTrackNil (line 1403) | func TestNegotiationNotNeededAfterReplaceTrackNil(t *testing.T) {
FILE: peerconnection_test.go
function newPair (line 21) | func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err e...
type signalPairOptions (line 35) | type signalPairOptions struct
function withModificationFunc (line 40) | func withModificationFunc(f func(string) string) func(*signalPairOptions) {
function withDisableInitialDataChannel (line 46) | func withDisableInitialDataChannel(disable bool) func(*signalPairOptions) {
function signalPairWithOptions (line 52) | func signalPairWithOptions(
function signalPairWithModification (line 105) | func signalPairWithModification(
function signalPair (line 117) | func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
function offerMediaHasDirection (line 125) | func offerMediaHasDirection(offer SessionDescription, kind RTPCodecType,...
function untilConnectionState (line 142) | func untilConnectionState(state PeerConnectionState, peers ...*PeerConne...
function TestNew (line 162) | func TestNew(t *testing.T) {
function TestPeerConnection_SetConfiguration (line 183) | func TestPeerConnection_SetConfiguration(t *testing.T) {
function TestPeerConnection_GetConfiguration (line 317) | func TestPeerConnection_GetConfiguration(t *testing.T) {
constant minimalOffer (line 343) | minimalOffer = `v=0
function TestSetRemoteDescription (line 365) | func TestSetRemoteDescription(t *testing.T) {
function TestCreateOfferAnswer (line 388) | func TestCreateOfferAnswer(t *testing.T) {
function TestPeerConnection_EventHandlers (line 419) | func TestPeerConnection_EventHandlers(t *testing.T) {
function TestMultipleOfferAnswer (line 513) | func TestMultipleOfferAnswer(t *testing.T) {
function TestNoFingerprintInFirstMediaIfSetRemoteDescription (line 535) | func TestNoFingerprintInFirstMediaIfSetRemoteDescription(t *testing.T) {
function TestNegotiationNeeded (line 577) | func TestNegotiationNeeded(t *testing.T) {
function TestMultipleCreateChannel (line 599) | func TestMultipleCreateChannel(t *testing.T) {
function TestGatherOnSetLocalDescription (line 648) | func TestGatherOnSetLocalDescription(t *testing.T) { //nolint:cyclop
function TestFlushOnSetLocalDescription (line 706) | func TestFlushOnSetLocalDescription(t *testing.T) {
function TestSetICECandidatePoolSizeLarge (line 788) | func TestSetICECandidatePoolSizeLarge(t *testing.T) {
function TestSetRemoteDescriptionInvalid (line 801) | func TestSetRemoteDescriptionInvalid(t *testing.T) {
function TestAddTransceiver (line 816) | func TestAddTransceiver(t *testing.T) {
function TestTransportChain (line 861) | func TestTransportChain(t *testing.T) {
function TestDTLSClose (line 875) | func TestDTLSClose(t *testing.T) {
function TestPeerConnection_SessionID (line 912) | func TestPeerConnection_SessionID(t *testing.T) {
function TestICETrickleCapabilityString (line 957) | func TestICETrickleCapabilityString(t *testing.T) {
FILE: peerconnectionstate.go
type PeerConnectionState (line 7) | type PeerConnectionState
method String (line 72) | func (t PeerConnectionState) String() string {
constant PeerConnectionStateUnknown (line 11) | PeerConnectionStateUnknown PeerConnectionState = iota
constant PeerConnectionStateNew (line 17) | PeerConnectionStateNew
constant PeerConnectionStateConnecting (line 22) | PeerConnectionStateConnecting
constant PeerConnectionStateConnected (line 27) | PeerConnectionStateConnected
constant PeerConnectionStateDisconnected (line 32) | PeerConnectionStateDisconnected
constant PeerConnectionStateFailed (line 36) | PeerConnectionStateFailed
constant PeerConnectionStateClosed (line 40) | PeerConnectionStateClosed
constant peerConnectionStateNewStr (line 45) | peerConnectionStateNewStr = "new"
constant peerConnectionStateConnectingStr (line 46) | peerConnectionStateConnectingStr = "connecting"
constant peerConnectionStateConnectedStr (line 47) | peerConnectionStateConnectedStr = "connected"
constant peerConnectionStateDisconnectedStr (line 48) | peerConnectionStateDisconnectedStr = "disconnected"
constant peerConnectionStateFailedStr (line 49) | peerConnectionStateFailedStr = "failed"
constant peerConnectionStateClosedStr (line 50) | peerConnectionStateClosedStr = "closed"
function newPeerConnectionState (line 53) | func newPeerConnectionState(raw string) PeerConnectionState {
FILE: peerconnectionstate_test.go
function TestNewPeerConnectionState (line 12) | func TestNewPeerConnectionState(t *testing.T) {
function TestPeerConnectionState_String (line 35) | func TestPeerConnectionState_String(t *testing.T) {
FILE: pkg/media/h264reader/h264reader.go
type H264Reader (line 14) | type H264Reader struct
method read (line 89) | func (reader *H264Reader) read(numToRead int) (data []byte, e error) {
method bitStreamStartsWithH264Prefix (line 108) | func (reader *H264Reader) bitStreamStartsWithH264Prefix() (prefixLengt...
method NextNAL (line 154) | func (reader *H264Reader) NextNAL() (*NAL, error) {
method processByte (line 203) | func (reader *H264Reader) processByte(readByte byte) (nalFound bool) {
function NewReader (line 30) | func NewReader(in io.Reader) (*H264Reader, error) {
type Option (line 48) | type Option
function NewReaderWithOptions (line 52) | func NewReaderWithOptions(in io.Reader, options ...Option) (*H264Reader,...
function WithIncludeSEI (line 69) | func WithIncludeSEI(include bool) Option {
type NAL (line 78) | type NAL struct
method parseHeader (line 234) | func (h *NAL) parseHeader() {
function newNal (line 230) | func newNal(data []byte) *NAL {
FILE: pkg/media/h264reader/h264reader_test.go
function CreateReader (line 14) | func CreateReader(h264 []byte, require *require.Assertions) *H264Reader {
function TestDataDoesNotStartWithH264Header (line 23) | func TestDataDoesNotStartWithH264Header(t *testing.T) {
function TestParseHeader (line 49) | func TestParseHeader(t *testing.T) {
function TestEOF (line 65) | func TestEOF(t *testing.T) {
function TestSkipSEI (line 86) | func TestSkipSEI(t *testing.T) {
function TestIncludeSEI (line 105) | func TestIncludeSEI(t *testing.T) {
function TestIssue1734_NextNal (line 131) | func TestIssue1734_NextNal(t *testing.T) {
function TestTrailing01AfterStartCode (line 152) | func TestTrailing01AfterStartCode(t *testing.T) {
FILE: pkg/media/h264reader/nalunittype.go
type NalUnitType (line 9) | type NalUnitType
method String (line 33) | func (n *NalUnitType) String() string { //nolint:cyclop
constant NalUnitTypeUnspecified (line 13) | NalUnitTypeUnspecified NalUnitType = 0
constant NalUnitTypeCodedSliceNonIdr (line 14) | NalUnitTypeCodedSliceNonIdr NalUnitType = 1
constant NalUnitTypeCodedSliceDataPartitionA (line 15) | NalUnitTypeCodedSliceDataPartitionA NalUnitType = 2
constant NalUnitTypeCodedSliceDataPartitionB (line 16) | NalUnitTypeCodedSliceDataPartitionB NalUnitType = 3
constant NalUnitTypeCodedSliceDataPartitionC (line 17) | NalUnitTypeCodedSliceDataPartitionC NalUnitType = 4
constant NalUnitTypeCodedSliceIdr (line 18) | NalUnitTypeCodedSliceIdr NalUnitType = 5
constant NalUnitTypeSEI (line 19) | NalUnitTypeSEI NalUnitType = 6
constant NalUnitTypeSPS (line 20) | NalUnitTypeSPS NalUnitType = 7
constant NalUnitTypePPS (line 21) | NalUnitTypePPS NalUnitType = 8
constant NalUnitTypeAUD (line 22) | NalUnitTypeAUD NalUnitType = 9
constant NalUnitTypeEndOfSequence (line 23) | NalUnitTypeEndOfSequence NalUnitType = 10
constant NalUnitTypeEndOfStream (line 24) | NalUnitTypeEndOfStream NalUnitType = 11
constant NalUnitTypeFiller (line 25) | NalUnitTypeFiller NalUnitType = 12
constant NalUnitTypeSpsExt (line 26) | NalUnitTypeSpsExt NalUnitType = 13
constant NalUnitTypeCodedSliceAux (line 27) | NalUnitTypeCodedSliceAux NalUnitType = 19
FILE: pkg/media/h264writer/h264writer.go
type H264Writer (line 23) | type H264Writer struct
method WriteRTP (line 48) | func (h *H264Writer) WriteRTP(packet *rtp.Packet) error {
method Close (line 75) | func (h *H264Writer) Close() error {
function New (line 31) | func New(filename string) (*H264Writer, error) {
function NewWith (line 41) | func NewWith(w io.Writer) *H264Writer {
function isKeyFrame (line 86) | func isKeyFrame(data []byte) bool {
FILE: pkg/media/h264writer/h264writer_test.go
type writerCloser (line 15) | type writerCloser struct
method Close (line 21) | func (w *writerCloser) Close() error {
function TestNewWith (line 25) | func TestNewWith(t *testing.T) {
function TestIsKeyFrame (line 31) | func TestIsKeyFrame(t *testing.T) {
function TestWriteRTP (line 62) | func TestWriteRTP(t *testing.T) {
type writerCounter (line 155) | type writerCounter struct
method Write (line 159) | func (w *writerCounter) Write([]byte) (int, error) {
method Close (line 165) | func (w *writerCounter) Close() error {
function TestNoZeroWrite (line 169) | func TestNoZeroWrite(t *testing.T) {
FILE: pkg/media/h265reader/h265reader.go
type H265Reader (line 14) | type H265Reader struct
method shouldSkipNAL (line 29) | func (reader *H265Reader) shouldSkipNAL(naluType NalUnitType) bool {
method read (line 100) | func (reader *H265Reader) read(numToRead int) (data []byte, e error) {
method bitStreamStartsWithH265Prefix (line 119) | func (reader *H265Reader) bitStreamStartsWithH265Prefix() (prefixLengt...
method NextNAL (line 165) | func (reader *H265Reader) NextNAL() (*NAL, error) {
method processByte (line 213) | func (reader *H265Reader) processByte(readByte byte) (nalFound bool) {
function NewReader (line 34) | func NewReader(in io.Reader) (*H265Reader, error) {
type Option (line 52) | type Option
function NewReaderWithOptions (line 56) | func NewReaderWithOptions(in io.Reader, options ...Option) (*H265Reader,...
function WithIncludeSEI (line 73) | func WithIncludeSEI(include bool) Option {
type NAL (line 82) | type NAL struct
method parseHeader (line 251) | func (h *NAL) parseHeader() {
function newNal (line 240) | func newNal(data []byte) *NAL {
FILE: pkg/media/h265reader/h265reader_test.go
function TestH265Reader_NextNAL (line 14) | func TestH265Reader_NextNAL(t *testing.T) {
function TestH265Reader_processByte (line 69) | func TestH265Reader_processByte(t *testing.T) {
function TestH265Reader_SEIPrefixSuffixSkippedByDefault (line 93) | func TestH265Reader_SEIPrefixSuffixSkippedByDefault(t *testing.T) {
function TestH265Reader_IncludeSEI (line 121) | func TestH265Reader_IncludeSEI(t *testing.T) {
function TestNAL_parseHeader (line 161) | func TestNAL_parseHeader(t *testing.T) {
FILE: pkg/media/h265reader/nalunittype.go
type NalUnitType (line 9) | type NalUnitType
method String (line 49) | func (n *NalUnitType) String() string { //nolint:cyclop
constant NalUnitTypeTrailN (line 14) | NalUnitTypeTrailN NalUnitType = 0
constant NalUnitTypeTrailR (line 15) | NalUnitTypeTrailR NalUnitType = 1
constant NalUnitTypeTsaN (line 16) | NalUnitTypeTsaN NalUnitType = 2
constant NalUnitTypeTsaR (line 17) | NalUnitTypeTsaR NalUnitType = 3
constant NalUnitTypeStsaN (line 18) | NalUnitTypeStsaN NalUnitType = 4
constant NalUnitTypeStsaR (line 19) | NalUnitTypeStsaR NalUnitType = 5
constant NalUnitTypeRadlN (line 20) | NalUnitTypeRadlN NalUnitType = 6
constant NalUnitTypeRadlR (line 21) | NalUnitTypeRadlR NalUnitType = 7
constant NalUnitTypeRaslN (line 22) | NalUnitTypeRaslN NalUnitType = 8
constant NalUnitTypeRaslR (line 23) | NalUnitTypeRaslR NalUnitType = 9
constant NalUnitTypeBlaWLp (line 24) | NalUnitTypeBlaWLp NalUnitType = 16
constant NalUnitTypeBlaWRadl (line 25) | NalUnitTypeBlaWRadl NalUnitType = 17
constant NalUnitTypeBlaNLp (line 26) | NalUnitTypeBlaNLp NalUnitType = 18
constant NalUnitTypeIdrWRadl (line 27) | NalUnitTypeIdrWRadl NalUnitType = 19
constant NalUnitTypeIdrNLp (line 28) | NalUnitTypeIdrNLp NalUnitType = 20
constant NalUnitTypeCraNut (line 29) | NalUnitTypeCraNut NalUnitType = 21
constant NalUnitTypeVps (line 32) | NalUnitTypeVps NalUnitType = 32
constant NalUnitTypeSps (line 33) | NalUnitTypeSps NalUnitType = 33
constant NalUnitTypePps (line 34) | NalUnitTypePps NalUnitType = 34
constant NalUnitTypeAud (line 35) | NalUnitTypeAud NalUnitType = 35
constant NalUnitTypeEos (line 36) | NalUnitTypeEos NalUnitType = 36
constant NalUnitTypeEob (line 37) | NalUnitTypeEob NalUnitType = 37
constant NalUnitTypeFd (line 38) | NalUnitTypeFd NalUnitType = 38
constant NalUnitTypePrefixSei (line 39) | NalUnitTypePrefixSei NalUnitType = 39
constant NalUnitTypeSuffixSei (line 40) | NalUnitTypeSuffixSei NalUnitType = 40
constant NalUnitTypeReserved41 (line 43) | NalUnitTypeReserved41 NalUnitType = 41
constant NalUnitTypeReserved47 (line 44) | NalUnitTypeReserved47 NalUnitType = 47
constant NalUnitTypeUnspec48 (line 45) | NalUnitTypeUnspec48 NalUnitType = 48
constant NalUnitTypeUnspec63 (line 46) | NalUnitTypeUnspec63 NalUnitType = 63
FILE: pkg/media/h265writer/h265writer.go
constant typeAP (line 19) | typeAP = 48
constant typeFU (line 20) | typeFU = 49
type H265Writer (line 25) | type H265Writer struct
method WriteRTP (line 49) | func (h *H265Writer) WriteRTP(packet *rtp.Packet) error {
method Close (line 76) | func (h *H265Writer) Close() error {
function New (line 32) | func New(filename string) (*H265Writer, error) {
function NewWith (line 42) | func NewWith(w io.Writer) *H265Writer {
function isKeyFrame (line 87) | func isKeyFrame(data []byte) bool {
function checkAggregationPacketForKeyFrame (line 116) | func checkAggregationPacketForKeyFrame(data []byte) bool {
function isKeyFrameNalu (line 151) | func isKeyFrameNalu(naluType h265reader.NalUnitType) bool {
FILE: pkg/media/h265writer/h265writer_test.go
function TestH265Writer_WriteRTP (line 14) | func TestH265Writer_WriteRTP(t *testing.T) {
function TestIsKeyFrame (line 38) | func TestIsKeyFrame(t *testing.T) {
function TestCheckAggregationPacketForKeyFrame (line 104) | func TestCheckAggregationPacketForKeyFrame(t *testing.T) {
FILE: pkg/media/ivfreader/ivfreader.go
constant ivfFileHeaderSignature (line 15) | ivfFileHeaderSignature = "DKIF"
constant ivfFileHeaderSize (line 16) | ivfFileHeaderSize = 32
constant ivfFrameHeaderSize (line 17) | ivfFrameHeaderSize = 12
type IVFFileHeader (line 32) | type IVFFileHeader struct
type IVFFrameHeader (line 47) | type IVFFrameHeader struct
type IVFReader (line 53) | type IVFReader struct
method ResetReader (line 87) | func (i *IVFReader) ResetReader(reset func(bytesRead int64) io.Reader) {
method ptsToTimestamp (line 91) | func (i *IVFReader) ptsToTimestamp(pts uint64) uint64 {
method ParseNextFrame (line 98) | func (i *IVFReader) ParseNextFrame() ([]byte, *IVFFrameHeader, error) {
method parseFileHeader (line 131) | func (i *IVFReader) parseFileHeader() (*IVFFileHeader, error) {
function NewWith (line 62) | func NewWith(stream io.Reader) (*IVFReader, *IVFFileHeader, error) {
FILE: pkg/media/ivfreader/ivfreader_test.go
function buildIVFContainer (line 15) | func buildIVFContainer(frames ...*[]byte) *bytes.Buffer {
function TestIVFReader_ParseValidFileHeader (line 38) | func TestIVFReader_ParseValidFileHeader(t *testing.T) {
function TestIVFReader_ErrorOnZeroTimebaseNumerator (line 58) | func TestIVFReader_ErrorOnZeroTimebaseNumerator(t *testing.T) {
function TestIVFReader_ParseValidFrames (line 72) | func TestIVFReader_ParseValidFrames(t *testing.T) {
function TestIVFReader_ParseIncompleteFrameHeader (line 128) | func TestIVFReader_ParseIncompleteFrameHeader(t *testing.T) {
function TestIVFReader_ParseIncompleteFramePayload (line 150) | func TestIVFReader_ParseIncompleteFramePayload(t *testing.T) {
function TestIVFReader_EOFWhenNoFramesLeft (line 173) | func TestIVFReader_EOFWhenNoFramesLeft(t *testing.T) {
FILE: pkg/media/ivfwriter/ivfwriter.go
type codec (line 28) | type codec
type IVFWriter (line 31) | type IVFWriter struct
method writeHeader (line 117) | func (i *IVFWriter) writeHeader() error {
method timestampToPts (line 147) | func (i *IVFWriter) timestampToPts(timestamp uint64) uint64 {
method writeFrame (line 151) | func (i *IVFWriter) writeFrame(frame []byte, timestamp uint64) error {
method WriteRTP (line 177) | func (i *IVFWriter) WriteRTP(packet *rtp.Packet) error {
method writeVP8 (line 209) | func (i *IVFWriter) writeVP8(packet *rtp.Packet, timestamp uint64) err...
method writeVP9 (line 240) | func (i *IVFWriter) writeVP9(packet *rtp.Packet, timestamp uint64) err...
method writeAV1 (line 272) | func (i *IVFWriter) writeAV1(packet *rtp.Packet, timestamp uint64) err...
method Close (line 312) | func (i *IVFWriter) Close() error {
constant codecUnset (line 56) | codecUnset codec = iota
constant codecVP8 (line 57) | codecVP8
constant codecVP9 (line 58) | codecVP9
constant codecAV1 (line 59) | codecAV1
constant mimeTypeVP8 (line 61) | mimeTypeVP8 = "video/VP8"
constant mimeTypeVP9 (line 62) | mimeTypeVP9 = "video/VP9"
constant mimeTypeAV1 (line 63) | mimeTypeAV1 = "video/AV1"
function New (line 67) | func New(fileName string, opts ...Option) (*IVFWriter, error) {
function NewWith (line 82) | func NewWith(out io.Writer, opts ...Option) (*IVFWriter, error) {
type Option (line 343) | type Option
function WithCodec (line 346) | func WithCodec(mimeType string) Option {
function WithWidthAndHeight (line 367) | func WithWidthAndHeight(width, height uint16) Option {
function WithFrameRate (line 376) | func WithFrameRate(numerator, denominator uint32) Option {
function WithDirectPTS (line 395) | func WithDirectPTS() Option {
FILE: pkg/media/ivfwriter/ivfwriter_test.go
type ivfWriterPacketTest (line 16) | type ivfWriterPacketTest struct
function TestIVFWriter_Basic (line 26) | func TestIVFWriter_Basic(t *testing.T) {
function TestIVFWriter_VP8 (line 81) | func TestIVFWriter_VP8(t *testing.T) {
function TestIVFWriter_EmptyPayload (line 240) | func TestIVFWriter_EmptyPayload(t *testing.T) {
function TestIVFWriter_Errors (line 249) | func TestIVFWriter_Errors(t *testing.T) {
function TestIVFWriter_AV1 (line 259) | func TestIVFWriter_AV1(t *testing.T) {
function TestIVFWriter_VP9 (line 355) | func TestIVFWriter_VP9(t *testing.T) {
function TestIVFWriter_WithWidthAndHeight (line 390) | func TestIVFWriter_WithWidthAndHeight(t *testing.T) {
function TestIVFWriter_WithFrameRate (line 405) | func TestIVFWriter_WithFrameRate(t *testing.T) {
function TestIVFWriter_WithDirectPTS (line 420) | func TestIVFWriter_WithDirectPTS(t *testing.T) {
function TestIVFWriter_DirectPTS_VP8 (line 432) | func TestIVFWriter_DirectPTS_VP8(t *testing.T) {
function TestIVFWriter_DirectPTS_Precision (line 501) | func TestIVFWriter_DirectPTS_Precision(t *testing.T) {
function TestIVFWriter_BackwardCompatibility (line 553) | func TestIVFWriter_BackwardCompatibility(t *testing.T) {
FILE: pkg/media/media.go
type Sample (line 14) | type Sample struct
type Writer (line 29) | type Writer interface
FILE: pkg/media/oggreader/oggreader.go
constant pageHeaderTypeBeginningOfStream (line 16) | pageHeaderTypeBeginningOfStream = 0x02
constant pageHeaderSignature (line 17) | pageHeaderSignature = "OggS"
constant idPageBasePayloadLength (line 19) | idPageBasePayloadLength = 19
constant pageHeaderLen (line 20) | pageHeaderLen = 27
type OggReader (line 36) | type OggReader struct
method readOpusHeader (line 185) | func (o *OggReader) readOpusHeader() (*OggHeader, error) {
method ParseNextPage (line 274) | func (o *OggReader) ParseNextPage() ([]byte, *OggPageHeader, error) { ...
method ResetReader (line 346) | func (o *OggReader) ResetReader(reset func(bytesRead int64) io.Reader) {
type OggHeader (line 51) | type OggHeader struct
function ParseOpusHead (line 66) | func ParseOpusHead(payload []byte) (*OggHeader, error) {
type OggPageHeader (line 83) | type OggPageHeader struct
method HeaderType (line 116) | func (p *OggPageHeader) HeaderType(payload []byte) (HeaderType, bool) {
type HeaderType (line 94) | type HeaderType
constant headerUnknown (line 97) | headerUnknown HeaderType = ""
constant HeaderOpusID (line 98) | HeaderOpusID HeaderType = "OpusHead"
constant HeaderOpusTags (line 99) | HeaderOpusTags HeaderType = "OpusTags"
function opusPayloadSignature (line 102) | func opusPayloadSignature(payload []byte) (HeaderType, bool) {
type Option (line 126) | type Option
function NewWith (line 135) | func NewWith(in io.Reader) (*OggReader, *OggHeader, error) {
function NewWithOptions (line 140) | func NewWithOptions(in io.Reader, options ...Option) (*OggReader, error) {
function WithDoChecksum (line 158) | func WithDoChecksum(doChecksum bool) Option {
function newWith (line 166) | func newWith(in io.Reader, doChecksum bool) (*OggReader, *OggHeader, err...
function validateOpusPageHeader (line 203) | func validateOpusPageHeader(pageHeader *OggPageHeader, payload []byte) e...
function parseBasicHeaderFields (line 223) | func parseBasicHeaderFields(payload []byte) *OggHeader {
function parseChannelMapping (line 238) | func parseChannelMapping(header *OggHeader, payload []byte) error {
function validatePayloadLength (line 251) | func validatePayloadLength(payload []byte, expectedLen int) error {
function parseExtendedChannelMapping (line 259) | func parseExtendedChannelMapping(header *OggHeader, payload []byte) error {
function generateChecksumTable (line 350) | func generateChecksumTable() *[256]uint32 {
type OpusTags (line 371) | type OpusTags struct
type UserComment (line 377) | type UserComment struct
function ParseOpusTags (line 384) | func ParseOpusTags(payload []byte) (*OpusTags, error) {
function validateOpusTagsHeader (line 411) | func validateOpusTagsHeader(payload []byte, minHeaderLen int) error {
function parseVendorString (line 424) | func parseVendorString(payload []byte, headerMagicLen, u32Size, minHeade...
function parseUserComments (line 442) | func parseUserComments(payload []byte, vendorEnd, u32Size int) ([]UserCo...
function parseSingleUserComment (line 464) | func parseSingleUserComment(payload []byte, pos, u32Size, index int) (Us...
FILE: pkg/media/oggreader/oggreader_test.go
function buildOggContainer (line 19) | func buildOggContainer() []byte {
function buildSurroundOggContainerShort (line 33) | func buildSurroundOggContainerShort() []byte {
function buildUnknownMappingFamilyContainer (line 44) | func buildUnknownMappingFamilyContainer(mappingFamily, channels uint8) [...
function buildChannelMappingFamilyContainer (line 78) | func buildChannelMappingFamilyContainer(
function TestOggReader_ParseValidHeader (line 116) | func TestOggReader_ParseValidHeader(t *testing.T) {
function TestOggReader_ParseNextPage (line 130) | func TestOggReader_ParseNextPage(t *testing.T) {
function TestOggReader_ParseErrors (line 147) | func TestOggReader_ParseErrors(t *testing.T) {
function TestOggReader_ChannelMappingFamily1 (line 204) | func TestOggReader_ChannelMappingFamily1(t *testing.T) {
function TestOggReader_KnownChannelMappingFamilies (line 256) | func TestOggReader_KnownChannelMappingFamilies(t *testing.T) {
function TestOggReader_ParseExtraFieldsForNonZeroMappingFamily (line 297) | func TestOggReader_ParseExtraFieldsForNonZeroMappingFamily(t *testing.T) {
function TestOggReader_NewWithOptions (line 348) | func TestOggReader_NewWithOptions(t *testing.T) {
function buildMultiTrackOggContainer (line 399) | func buildMultiTrackOggContainer(
function TestOggReader_MultiTrackFile (line 478) | func TestOggReader_MultiTrackFile(t *testing.T) {
function buildOpusTagsPayload (line 540) | func buildOpusTagsPayload(vendor string, comments []UserComment) []byte {
function buildOggPage (line 569) | func buildOggPage(serial uint32, pageIndex uint32, headerType uint8, pay...
function buildOpusHeadPayload (line 600) | func buildOpusHeadPayload(
function buildTwoTrackOggContainer (line 630) | func buildTwoTrackOggContainer(
function processPages (line 656) | func processPages(reader *OggReader) ([]HeaderType, []*OpusTags, error) {
function countHeaderTypes (line 690) | func countHeaderTypes(headersFound []HeaderType) (int, int) {
function userCommentsToMap (line 706) | func userCommentsToMap(comments []UserComment) map[string]string {
function TestOggReader_DetectHeadersAndTags (line 715) | func TestOggReader_DetectHeadersAndTags(t *testing.T) {
function TestParseOpusTagsErrors (line 762) | func TestParseOpusTagsErrors(t *testing.T) {
function TestParseVendorStringMissingCommentCount (line 858) | func TestParseVendorStringMissingCommentCount(t *testing.T) {
function TestParseOpusHead_EmptyPayload_NoPanic (line 878) | func TestParseOpusHead_EmptyPayload_NoPanic(t *testing.T) {
function TestParseOpusHead_ChannelMappingSliceOverflow_NoPanic (line 883) | func TestParseOpusHead_ChannelMappingSliceOverflow_NoPanic(t *testing.T) {
function makeOpusHeadWithChannelMapping (line 894) | func makeOpusHeadWithChannelMapping(channels uint8, mappingFamily uint8)...
FILE: pkg/media/oggwriter/oggwriter.go
constant pageHeaderTypeContinuationOfStream (line 19) | pageHeaderTypeContinuationOfStream = 0x00
constant pageHeaderTypeBeginningOfStream (line 20) | pageHeaderTypeBeginningOfStream = 0x02
constant pageHeaderTypeEndOfStream (line 21) | pageHeaderTypeEndOfStream = 0x04
constant defaultPreSkip (line 22) | defaultPreSkip = 3840
constant idPageSignature (line 23) | idPageSignature = "OpusHead"
constant commentPageSignature (line 24) | commentPageSignature = "OpusTags"
constant pageHeaderSignature (line 25) | pageHeaderSignature = "OggS"
type OggWriter (line 34) | type OggWriter struct
method writeHeaders (line 110) | func (i *OggWriter) writeHeaders() error {
method createPage (line 152) | func (i *OggWriter) createPage(payload []uint8, headerType uint8, gran...
method WriteRTP (line 189) | func (i *OggWriter) WriteRTP(packet *rtp.Packet) error {
method Close (line 219) | func (i *OggWriter) Close() error {
method writeToStream (line 259) | func (i *OggWriter) writeToStream(p []byte) error {
function New (line 48) | func New(fileName string, sampleRate uint32, channelCount uint16) (*OggW...
function NewWith (line 63) | func NewWith(out io.Writer, sampleRate uint32, channelCount uint16) (*Og...
constant pageHeaderSize (line 149) | pageHeaderSize = 27
function generateChecksumTable (line 269) | func generateChecksumTable() *[256]uint32 {
FILE: pkg/media/oggwriter/oggwriter_test.go
type oggWriterPacketTest (line 15) | type oggWriterPacketTest struct
function TestOggWriter_AddPacketAndClose (line 25) | func TestOggWriter_AddPacketAndClose(t *testing.T) {
function TestOggWriter_EmptyPayload (line 128) | func TestOggWriter_EmptyPayload(t *testing.T) {
function TestOggWriter_LargePayload (line 137) | func TestOggWriter_LargePayload(t *testing.T) {
FILE: pkg/media/rtpdump/reader.go
type Reader (line 15) | type Reader struct
method Next (line 70) | func (r *Reader) Next() (Packet, error) {
function NewReader (line 22) | func NewReader(r io.Reader) (*Reader, Header, error) {
FILE: pkg/media/rtpdump/reader_test.go
function TestReader (line 17) | func TestReader(t *testing.T) { //nolint:maintidx
FILE: pkg/media/rtpdump/rtpdump.go
constant pktHeaderLen (line 16) | pktHeaderLen = 8
constant headerLen (line 17) | headerLen = 16
constant preambleLen (line 18) | preambleLen = 36
type Header (line 26) | type Header struct
method Marshal (line 36) | func (h Header) Marshal() ([]byte, error) {
method Unmarshal (line 56) | func (h *Header) Unmarshal(data []byte) error {
type Packet (line 79) | type Packet struct
method Marshal (line 90) | func (p Packet) Marshal() ([]byte, error) {
method Unmarshal (line 110) | func (p *Packet) Unmarshal(data []byte) error {
method offsetMs (line 130) | func (p *Packet) offsetMs() uint32 {
type packetHeader (line 134) | type packetHeader struct
method Marshal (line 144) | func (p packetHeader) Marshal() ([]byte, error) {
method Unmarshal (line 154) | func (p *packetHeader) Unmarshal(d []byte) error {
method offset (line 166) | func (p packetHeader) offset() time.Duration {
FILE: pkg/media/rtpdump/rtpdump_test.go
function TestHeaderRoundTrip (line 14) | func TestHeaderRoundTrip(t *testing.T) {
function TestMarshalHeader (line 42) | func TestMarshalHeader(t *testing.T) {
function TestPacketRoundTrip (line 70) | func TestPacketRoundTrip(t *testing.T) {
FILE: pkg/media/rtpdump/writer.go
type Writer (line 13) | type Writer struct
method WritePacket (line 41) | func (w *Writer) WritePacket(p Packet) error {
function NewWriter (line 20) | func NewWriter(w io.Writer, hdr Header) (*Writer, error) {
FILE: pkg/media/rtpdump/writer_test.go
function TestWriter (line 17) | func TestWriter(t *testing.T) {
function TestRoundTrip (line 49) | func TestRoundTrip(t *testing.T) {
FILE: pkg/media/samplebuilder/sampleSequenceLocation.go
type sampleSequenceLocation (line 7) | type sampleSequenceLocation struct
method empty (line 15) | func (l sampleSequenceLocation) empty() bool {
method hasData (line 19) | func (l sampleSequenceLocation) hasData() bool {
method count (line 23) | func (l sampleSequenceLocation) count() uint16 {
method compare (line 34) | func (l sampleSequenceLocation) compare(pos uint16) int {
constant slCompareVoid (line 28) | slCompareVoid = iota
constant slCompareBefore (line 29) | slCompareBefore
constant slCompareInside (line 30) | slCompareInside
constant slCompareAfter (line 31) | slCompareAfter
FILE: pkg/media/samplebuilder/sampleSequenceLocation_test.go
function TestSampleSequenceLocationCompare (line 12) | func TestSampleSequenceLocationCompare(t *testing.T) {
FILE: pkg/media/samplebuilder/samplebuilder.go
type SampleBuilder (line 16) | type SampleBuilder struct
method tooOld (line 71) | func (s *SampleBuilder) tooOld(location sampleSequenceLocation) bool {
method fetchTimestamp (line 107) | func (s *SampleBuilder) fetchTimestamp(location sampleSequenceLocation...
method releasePacket (line 119) | func (s *SampleBuilder) releasePacket(i uint16) {
method purgeConsumedBuffers (line 129) | func (s *SampleBuilder) purgeConsumedBuffers() {
method purgeConsumedLocation (line 135) | func (s *SampleBuilder) purgeConsumedLocation(consume sampleSequenceLo...
method purgeBuffers (line 155) | func (s *SampleBuilder) purgeBuffers(flush bool) {
method Push (line 185) | func (s *SampleBuilder) Push(packet *rtp.Packet) {
method Flush (line 203) | func (s *SampleBuilder) Flush() {
method buildSample (line 214) | func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample {
method Pop (line 336) | func (s *SampleBuilder) Pop() *media.Sample {
function New (line 62) | func New(maxLate uint16, depacketizer rtp.Depacketizer, sampleRate uint3...
constant secondToNanoseconds (line 207) | secondToNanoseconds = 1000000000
function seqnumDistance (line 349) | func seqnumDistance(x, y uint16) uint16 {
function timestampDistance (line 359) | func timestampDistance(x, y uint32) uint32 {
type Option (line 369) | type Option
function WithPacketReleaseHandler (line 373) | func WithPacketReleaseHandler(h func(*rtp.Packet)) Option {
function WithPacketHeadHandler (line 381) | func WithPacketHeadHandler(h func(headPacket any) any) Option {
function WithMaxTimeDelay (line 389) | func WithMaxTimeDelay(maxLateDuration time.Duration) Option {
function WithRTPHeaders (line 398) | func WithRTPHeaders(enable bool) Option {
FILE: pkg/media/samplebuilder/samplebuilder_test.go
type sampleBuilderTest (line 19) | type sampleBuilderTest struct
type fakeDepacketizer (line 30) | type fakeDepacketizer struct
method Unmarshal (line 36) | func (f *fakeDepacketizer) Unmarshal(r []byte) ([]byte, error) {
method IsPartitionHead (line 40) | func (f *fakeDepacketizer) IsPartitionHead(payload []byte) bool {
method IsPartitionTail (line 59) | func (f *fakeDepacketizer) IsPartitionTail(marker bool, _ []byte) bool {
function TestSampleBuilder (line 63) | func TestSampleBuilder(t *testing.T) { //nolint:maintidx
function TestSampleBuilderMaxLate (line 349) | func TestSampleBuilderMaxLate(t *testing.T) {
function TestSeqnumDistance (line 387) | func TestSeqnumDistance(t *testing.T) {
function TestSampleBuilderCleanReference (line 406) | func TestSampleBuilderCleanReference(t *testing.T) {
function TestSampleBuilderPushMaxZero (line 441) | func TestSampleBuilderPushMaxZero(t *testing.T) {
function TestSampleBuilderWithPacketReleaseHandler (line 456) | func TestSampleBuilderWithPacketReleaseHandler(t *testing.T) {
function TestSampleBuilderWithPacketHeadHandler (line 484) | func TestSampleBuilderWithPacketHeadHandler(t *testing.T) {
function TestSampleBuilderData (line 517) | func TestSampleBuilderData(t *testing.T) {
function TestSampleBuilderPacketUnreference (line 547) | func TestSampleBuilderPacketUnreference(t *testing.T) {
function TestSampleBuilder_Flush (line 586) | func TestSampleBuilder_Flush(t *testing.T) {
function BenchmarkSampleBuilderSequential (line 623) | func BenchmarkSampleBuilderSequential(b *testing.B) {
function BenchmarkSampleBuilderLoss (line 649) | func BenchmarkSampleBuilderLoss(b *testing.B) {
function BenchmarkSampleBuilderReordered (line 678) | func BenchmarkSampleBuilderReordered(b *testing.B) {
function BenchmarkSampleBuilderFragmented (line 704) | func BenchmarkSampleBuilderFragmented(b *testing.B) {
function BenchmarkSampleBuilderFragmentedLoss (line 730) | func BenchmarkSampleBuilderFragmentedLoss(b *testing.B) {
FILE: pkg/null/null.go
type Bool (line 9) | type Bool struct
function NewBool (line 15) | func NewBool(value bool) Bool {
type Byte (line 20) | type Byte struct
function NewByte (line 26) | func NewByte(value byte) Byte {
type Complex128 (line 31) | type Complex128 struct
function NewComplex128 (line 37) | func NewComplex128(value complex128) Complex128 {
type Complex64 (line 42) | type Complex64 struct
function NewComplex64 (line 48) | func NewComplex64(value complex64) Complex64 {
type Float32 (line 53) | type Float32 struct
function NewFloat32 (line 59) | func NewFloat32(value float32) Float32 {
type Float64 (line 64) | type Float64 struct
function NewFloat64 (line 70) | func NewFloat64(value float64) Float64 {
type Int (line 75) | type Int struct
function NewInt (line 81) | func NewInt(value int) Int {
type Int16 (line 86) | type Int16 struct
function NewInt16 (line 92) | func NewInt16(value int16) Int16 {
type Int32 (line 97) | type Int32 struct
function NewInt32 (line 103) | func NewInt32(value int32) Int32 {
type Int64 (line 108) | type Int64 struct
function NewInt64 (line 114) | func NewInt64(value int64) Int64 {
type Int8 (line 119) | type Int8 struct
function NewInt8 (line 125) | func NewInt8(value int8) Int8 {
type Rune (line 130) | type Rune struct
function NewRune (line 136) | func NewRune(value rune) Rune {
type String (line 141) | type String struct
function NewString (line 147) | func NewString(value string) String {
type Uint (line 152) | type Uint struct
function NewUint (line 158) | func NewUint(value uint) Uint {
type Uint16 (line 163) | type Uint16 struct
function NewUint16 (line 169) | func NewUint16(value uint16) Uint16 {
type Uint32 (line 174) | type Uint32 struct
function NewUint32 (line 180) | func NewUint32(value uint32) Uint32 {
type Uint64 (line 185) | type Uint64 struct
function NewUint64 (line 191) | func NewUint64(value uint64) Uint64 {
type Uint8 (line 196) | type Uint8 struct
function NewUint8 (line 202) | func NewUint8(value uint8) Uint8 {
FILE: pkg/null/null_test.go
function TestNewBool (line 12) | func TestNewBool(t *testing.T) {
function TestNewByte (line 29) | func TestNewByte(t *testing.T) {
function TestNewComplex128 (line 46) | func TestNewComplex128(t *testing.T) {
function TestNewComplex64 (line 63) | func TestNewComplex64(t *testing.T) {
function TestNewFloat32 (line 80) | func TestNewFloat32(t *testing.T) {
function TestNewFloat64 (line 97) | func TestNewFloat64(t *testing.T) {
function TestNewInt (line 114) | func TestNewInt(t *testing.T) {
function TestNewInt16 (line 131) | func TestNewInt16(t *testing.T) {
function TestNewInt32 (line 148) | func TestNewInt32(t *testing.T) {
function TestNewInt64 (line 165) | func TestNewInt64(t *testing.T) {
function TestNewInt8 (line 182) | func TestNewInt8(t *testing.T) {
function TestNewRune (line 199) | func TestNewRune(t *testing.T) {
function TestNewString (line 216) | func TestNewString(t *testing.T) {
function TestNewUint (line 233) | func TestNewUint(t *testing.T) {
function TestNewUint16 (line 250) | func TestNewUint16(t *testing.T) {
function TestNewUint32 (line 267) | func TestNewUint32(t *testing.T) {
function TestNewUint64 (line 284) | func TestNewUint64(t *testing.T) {
function TestNewUint8 (line 301) | func TestNewUint8(t *testing.T) {
FILE: pkg/rtcerr/errors.go
type UnknownError (line 13) | type UnknownError struct
method Error (line 17) | func (e *UnknownError) Error() string {
method Unwrap (line 23) | func (e *UnknownError) Unwrap() error {
type InvalidStateError (line 28) | type InvalidStateError struct
method Error (line 32) | func (e *InvalidStateError) Error() string {
method Unwrap (line 38) | func (e *InvalidStateError) Unwrap() error {
type InvalidAccessError (line 44) | type InvalidAccessError struct
method Error (line 48) | func (e *InvalidAccessError) Error() string {
method Unwrap (line 54) | func (e *InvalidAccessError) Unwrap() error {
type NotSupportedError (line 59) | type NotSupportedError struct
method Error (line 63) | func (e *NotSupportedError) Error() string {
method Unwrap (line 69) | func (e *NotSupportedError) Unwrap() error {
type InvalidModificationError (line 74) | type InvalidModificationError struct
method Error (line 78) | func (e *InvalidModificationError) Error() string {
method Unwrap (line 84) | func (e *InvalidModificationError) Unwrap() error {
type SyntaxError (line 89) | type SyntaxError struct
method Error (line 93) | func (e *SyntaxError) Error() string {
method Unwrap (line 99) | func (e *SyntaxError) Unwrap() error {
type TypeError (line 104) | type TypeError struct
method Error (line 108) | func (e *TypeError) Error() string {
method Unwrap (line 114) | func (e *TypeError) Unwrap() error {
type OperationError (line 120) | type OperationError struct
method Error (line 124) | func (e *OperationError) Error() string {
method Unwrap (line 130) | func (e *OperationError) Unwrap() error {
type NotReadableError (line 135) | type NotReadableError struct
method Error (line 139) | func (e *NotReadableError) Error() string {
method Unwrap (line 145) | func (e *NotReadableError) Unwrap() error {
type RangeError (line 151) | type RangeError struct
method Error (line 155) | func (e *RangeError) Error() string {
method Unwrap (line 161) | func (e *RangeError) Unwrap() error {
FILE: rtcpfeedback.go
constant TypeRTCPFBTransportCC (line 8) | TypeRTCPFBTransportCC = "transport-cc"
constant TypeRTCPFBGoogREMB (line 11) | TypeRTCPFBGoogREMB = "goog-remb"
constant TypeRTCPFBACK (line 14) | TypeRTCPFBACK = "ack"
constant TypeRTCPFBCCM (line 17) | TypeRTCPFBCCM = "ccm"
constant TypeRTCPFBNACK (line 20) | TypeRTCPFBNACK = "nack"
type RTCPFeedback (line 25) | type RTCPFeedback struct
FILE: rtcpmuxpolicy.go
type RTCPMuxPolicy (line 12) | type RTCPMuxPolicy
method String (line 47) | func (t RTCPMuxPolicy) String() string {
method UnmarshalJSON (line 59) | func (t *RTCPMuxPolicy) UnmarshalJSON(b []byte) error {
method MarshalJSON (line 71) | func (t RTCPMuxPolicy) MarshalJSON() ([]byte, error) {
constant RTCPMuxPolicyUnknown (line 16) | RTCPMuxPolicyUnknown RTCPMuxPolicy = iota
constant RTCPMuxPolicyNegotiate (line 22) | RTCPMuxPolicyNegotiate
constant RTCPMuxPolicyRequire (line 27) | RTCPMuxPolicyRequire
constant rtcpMuxPolicyNegotiateStr (line 32) | rtcpMuxPolicyNegotiateStr = "negotiate"
constant rtcpMuxPolicyRequireStr (line 33) | rtcpMuxPolicyRequireStr = "require"
function newRTCPMuxPolicy (line 36) | func newRTCPMuxPolicy(raw string) RTCPMuxPolicy {
FILE: rtcpmuxpolicy_test.go
function TestNewRTCPMuxPolicy (line 12) | func TestNewRTCPMuxPolicy(t *testing.T) {
function TestRTCPMuxPolicy_String (line 31) | func TestRTCPMuxPolicy_String(t *testing.T) {
FILE: rtpcapabilities.go
type RTPCapabilities (line 9) | type RTPCapabilities struct
FILE: rtpcodec.go
type RTPCodecType (line 15) | type RTPCodecType
method String (line 28) | func (t RTPCodecType) String() string {
constant RTPCodecTypeUnknown (line 19) | RTPCodecTypeUnknown RTPCodecType = iota
constant RTPCodecTypeAudio (line 22) | RTPCodecTypeAudio
constant RTPCodecTypeVideo (line 25) | RTPCodecTypeVideo
function NewRTPCodecType (line 40) | func NewRTPCodecType(r string) RTPCodecType {
type RTPCodecCapability (line 54) | type RTPCodecCapability struct
type RTPHeaderExtensionCapability (line 65) | type RTPHeaderExtensionCapability struct
type RTPHeaderExtensionParameter (line 72) | type RTPHeaderExtensionParameter struct
type RTPCodecParameters (line 82) | type RTPCodecParameters struct
type RTPParameters (line 92) | type RTPParameters struct
type codecMatchType (line 97) | type codecMatchType
constant codecMatchNone (line 100) | codecMatchNone codecMatchType = 0
constant codecMatchPartial (line 101) | codecMatchPartial codecMatchType = 1
constant codecMatchExact (line 102) | codecMatchExact codecMatchType = 2
function codecParametersFuzzySearch (line 108) | func codecParametersFuzzySearch(
function findRTXPayloadType (line 148) | func findRTXPayloadType(needle PayloadType, haystack []RTPCodecParameter...
function primaryPayloadTypeForRTXExists (line 161) | func primaryPayloadTypeForRTXExists(needle RTPCodecParameters, haystack ...
function filterUnattachedRTX (line 192) | func filterUnattachedRTX(codecs []RTPCodecParameters) []RTPCodecParamete...
function findFECPayloadType (line 205) | func findFECPayloadType(haystack []RTPCodecParameters) PayloadType {
function rtcpFeedbackIntersection (line 215) | func rtcpFeedbackIntersection(a, b []RTCPFeedback) (out []RTCPFeedback) {
FILE: rtpcodec_test.go
function TestFindPrimaryPayloadTypeForRTX (line 12) | func TestFindPrimaryPayloadTypeForRTX(t *testing.T) {
function TestFindFECPayloadType (line 205) | func TestFindFECPayloadType(t *testing.T) {
FILE: rtpcodingparameters.go
type RTPRtxParameters (line 8) | type RTPRtxParameters struct
type RTPFecParameters (line 14) | type RTPFecParameters struct
type RTPCodingParameters (line 21) | type RTPCodingParameters struct
FILE: rtpdecodingparameters.go
type RTPDecodingParameters (line 9) | type RTPDecodingParameters struct
FILE: rtpencodingparameters.go
type RTPEncodingParameters (line 9) | type RTPEncodingParameters struct
FILE: rtpreceiveparameters.go
type RTPReceiveParameters (line 7) | type RTPReceiveParameters struct
FILE: rtpreceiver.go
type trackStreams (line 27) | type trackStreams struct
type rtxPacketWithAttributes (line 46) | type rtxPacketWithAttributes struct
method release (line 52) | func (p *rtxPacketWithAttributes) release() {
type RTPReceiver (line 61) | type RTPReceiver struct
method setRTPTransceiver (line 103) | func (r *RTPReceiver) setRTPTransceiver(tr *RTPTransceiver) {
method Transport (line 111) | func (r *RTPReceiver) Transport() *DTLSTransport {
method getParameters (line 118) | func (r *RTPReceiver) getParameters() RTPParameters {
method GetParameters (line 132) | func (r *RTPReceiver) GetParameters() RTPParameters {
method Track (line 140) | func (r *RTPReceiver) Track() *TrackRemote {
method Tracks (line 153) | func (r *RTPReceiver) Tracks() []*TrackRemote {
method RTPTransceiver (line 167) | func (r *RTPReceiver) RTPTransceiver() *RTPTransceiver {
method configureReceive (line 175) | func (r *RTPReceiver) configureReceive(parameters RTPReceiveParameters) {
method startReceive (line 195) | func (r *RTPReceiver) startReceive(parameters RTPReceiveParameters) er...
method Receive (line 284) | func (r *RTPReceiver) Receive(parameters RTPReceiveParameters) error {
method Read (line 291) | func (r *RTPReceiver) Read(b []byte) (n int, a interceptor.Attributes,...
method ReadSimulcast (line 305) | func (r *RTPReceiver) ReadSimulcast(b []byte, rid string) (n int, a in...
method ReadRTCP (line 331) | func (r *RTPReceiver) ReadRTCP() ([]rtcp.Packet, interceptor.Attribute...
method ReadSimulcastRTCP (line 347) | func (r *RTPReceiver) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, in...
method haveReceived (line 359) | func (r *RTPReceiver) haveReceived() bool {
method haveClosed (line 368) | func (r *RTPReceiver) haveClosed() bool {
method Stop (line 373) | func (r *RTPReceiver) Stop() error { //nolint:cyclop
method collectStats (line 424) | func (r *RTPReceiver) collectStats(collector *statsReportCollector, st...
method populateInboundStats (line 474) | func (r *RTPReceiver) populateInboundStats(
method collectAudioPlayoutStats (line 508) | func (r *RTPReceiver) collectAudioPlayoutStats(
method streamsForTrack (line 520) | func (r *RTPReceiver) streamsForTrack(t *TrackRemote) *trackStreams {
method readRTP (line 531) | func (r *RTPReceiver) readRTP(b []byte, reader *TrackRemote) (n int, a...
method receiveForRid (line 547) | func (r *RTPReceiver) receiveForRid(
method receiveForRtx (line 588) | func (r *RTPReceiver) receiveForRtx(
method receiveForRtxInternal (line 612) | func (r *RTPReceiver) receiveForRtxInternal(
method SetReadDeadline (line 715) | func (r *RTPReceiver) SetReadDeadline(t time.Time) error {
method SetReadDeadlineSimulcast (line 724) | func (r *RTPReceiver) SetReadDeadlineSimulcast(deadline time.Time, rid...
method setRTPReadDeadline (line 739) | func (r *RTPReceiver) setRTPReadDeadline(deadline time.Time, reader *T...
method readRTX (line 751) | func (r *RTPReceiver) readRTX(reader *TrackRemote) *rtxPacketWithAttri...
method NewRTPReceiver (line 82) | func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTranspo...
FILE: rtpreceiver_go.go
method SetRTPParameters (line 16) | func (r *RTPReceiver) SetRTPParameters(params RTPParameters) {
FILE: rtpreceiver_go_test.go
function TestSetRTPParameters (line 19) | func TestSetRTPParameters(t *testing.T) {
function TestReceiveError (line 78) | func TestReceiveError(t *testing.T) {
FILE: rtpreceiver_js.go
type RTPReceiver (line 12) | type RTPReceiver struct
method JSValue (line 18) | func (r *RTPReceiver) JSValue() js.Value {
FILE: rtpreceiver_test.go
function Test_RTPReceiver_SetReadDeadline (line 29) | func Test_RTPReceiver_SetReadDeadline(t *testing.T) {
function TestRTPReceiver_ClosedReceiveForRIDAndRTX (line 75) | func TestRTPReceiver_ClosedReceiveForRIDAndRTX(t *testing.T) {
function TestRTPReceiver_readRTX_ChannelAccessSafe (line 142) | func TestRTPReceiver_readRTX_ChannelAccessSafe(t *testing.T) {
function TestRTPReceiver_ReadRTP_SimulcastNoRace (line 209) | func TestRTPReceiver_ReadRTP_SimulcastNoRace(t *testing.T) {
function TestRTPReceiver_CollectStats_Mapping (line 316) | func TestRTPReceiver_CollectStats_Mapping(t *testing.T) {
function TestRTPReceiver_CollectStats_AudioPlayoutPull (line 382) | func TestRTPReceiver_CollectStats_AudioPlayoutPull(t *testing.T) {
function TestRTPReceiver_CollectStats_AudioPlayoutSharedProvider (line 421) | func TestRTPReceiver_CollectStats_AudioPlayoutSharedProvider(t *testing....
function TestRTPReceiver_CollectStats_AudioPlayoutTimestampAlignment (line 461) | func TestRTPReceiver_CollectStats_AudioPlayoutTimestampAlignment(t *test...
type fakeGetter (line 494) | type fakeGetter struct
method Get (line 496) | func (f *fakeGetter) Get(uint32) *stats.Stats { return &f.s }
type fakeAudioPlayoutStatsProvider (line 498) | type fakeAudioPlayoutStatsProvider struct
method Snapshot (line 506) | func (f *fakeAudioPlayoutStatsProvider) Snapshot(now time.Time) (Audio...
method AddTrack (line 513) | func (f *fakeAudioPlayoutStatsProvider) AddTrack(track *TrackRemote) e...
method RemoveTrack (line 519) | func (f *fakeAudioPlayoutStatsProvider) RemoveTrack(track *TrackRemote) {
function TestRTPReceiverRTXStreamInfoMimeType (line 523) | func TestRTPReceiverRTXStreamInfoMimeType(t *testing.T) {
function mimeTypes (line 594) | func mimeTypes(infos []*interceptor.StreamInfo) []string {
function TestRTPReceiver_CollectStats_RID (line 605) | func TestRTPReceiver_CollectStats_RID(t *testing.T) {
FILE: rtpsender.go
type trackEncoding (line 21) | type trackEncoding struct
type RTPSender (line 35) | type RTPSender struct
method isNegotiated (line 86) | func (r *RTPSender) isNegotiated() bool {
method setNegotiated (line 93) | func (r *RTPSender) setNegotiated() {
method setRTPTransceiver (line 99) | func (r *RTPSender) setRTPTransceiver(rtpTransceiver *RTPTransceiver) {
method Transport (line 107) | func (r *RTPSender) Transport() *DTLSTransport {
method GetParameters (line 116) | func (r *RTPSender) GetParameters() RTPSendParameters {
method AddEncoding (line 153) | func (r *RTPSender) AddEncoding(track TrackLocal) error { //nolint:cyclop
method addEncoding (line 200) | func (r *RTPSender) addEncoding(track TrackLocal) {
method Track (line 218) | func (r *RTPSender) Track() TrackLocal {
method ReplaceTrack (line 232) | func (r *RTPSender) ReplaceTrack(track TrackLocal) error { //nolint:cy...
method Send (line 301) | func (r *RTPSender) Send(parameters RTPSendParameters) error {
method Stop (line 378) | func (r *RTPSender) Stop() error {
method Read (line 410) | func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, e...
method ReadRTCP (line 420) | func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes,...
method ReadSimulcast (line 436) | func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a inte...
method ReadSimulcastRTCP (line 457) | func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, inte...
method SetReadDeadline (line 471) | func (r *RTPSender) SetReadDeadline(t time.Time) error {
method SetReadDeadlineSimulcast (line 481) | func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid s...
method hasSent (line 495) | func (r *RTPSender) hasSent() bool {
method hasStopped (line 505) | func (r *RTPSender) hasStopped() bool {
method configureRTXAndFEC (line 516) | func (r *RTPSender) configureRTXAndFEC() {
method NewRTPSender (line 60) | func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport)...
FILE: rtpsender_js.go
type RTPSender (line 12) | type RTPSender struct
method JSValue (line 18) | func (s *RTPSender) JSValue() js.Value {
FILE: rtpsender_test.go
function Test_RTPSender_ReplaceTrack (line 22) | func Test_RTPSender_ReplaceTrack(t *testing.T) { //nolint:cyclop
function Test_RTPSender_GetParameters (line 103) | func Test_RTPSender_GetParameters(t *testing.T) {
function Test_RTPSender_GetParameters_WithRID (line 127) | func Test_RTPSender_GetParameters_WithRID(t *testing.T) {
function Test_RTPSender_SetReadDeadline (line 156) | func Test_RTPSender_SetReadDeadline(t *testing.T) {
function Test_RTPSender_ReplaceTrack_InvalidTrackKindChange (line 185) | func Test_RTPSender_ReplaceTrack_InvalidTrackKindChange(t *testing.T) {
function Test_RTPSender_ReplaceTrack_InvalidCodecChange (line 227) | func Test_RTPSender_ReplaceTrack_InvalidCodecChange(t *testing.T) {
function Test_RTPSender_GetParameters_NilTrack (line 275) | func Test_RTPSender_GetParameters_NilTrack(t *testing.T) {
function Test_RTPSender_Send (line 291) | func Test_RTPSender_Send(t *testing.T) {
function Test_RTPSender_Send_Called_Once (line 309) | func Test_RTPSender_Send_Called_Once(t *testing.T) {
function Test_RTPSender_Send_Track_Removed (line 330) | func Test_RTPSender_Send_Track_Removed(t *testing.T) {
function Test_RTPSender_Add_Encoding (line 347) | func Test_RTPSender_Add_Encoding(t *testing.T) {
function Test_RTPSender_FEC_Support (line 425) | func Test_RTPSender_FEC_Support(t *testing.T) {
function Test_RTPSender_RTX_Support (line 468) | func Test_RTPSender_RTX_Support(t *testing.T) {
type TrackLocalCheckRTCPReaderOnBind (line 506) | type TrackLocalCheckRTCPReaderOnBind struct
method Bind (line 512) | func (s *TrackLocalCheckRTCPReaderOnBind) Bind(ctx TrackLocalContext) ...
function Test_RTPSender_RTCPReader_Bind_Not_Nil (line 520) | func Test_RTPSender_RTCPReader_Bind_Not_Nil(t *testing.T) {
function Test_RTPSender_SetReadDeadline_Crash (line 544) | func Test_RTPSender_SetReadDeadline_Crash(t *testing.T) {
FILE: rtpsendparameters.go
type RTPSendParameters (line 7) | type RTPSendParameters struct
FILE: rtptransceiver.go
type RTPTransceiver (line 20) | type RTPTransceiver struct
method SetCodecPreferences (line 54) | func (t *RTPTransceiver) SetCodecPreferences(codecs []RTPCodecParamete...
method getCodecs (line 72) | func (t *RTPTransceiver) getCodecs() []RTPCodecParameters {
method setCodecPreferencesFromRemoteDescription (line 97) | func (t *RTPTransceiver) setCodecPreferencesFromRemoteDescription(medi...
method Sender (line 186) | func (t *RTPTransceiver) Sender() *RTPSender {
method SetSender (line 195) | func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) err...
method setSender (line 201) | func (t *RTPTransceiver) setSender(s *RTPSender) {
method Receiver (line 214) | func (t *RTPTransceiver) Receiver() *RTPReceiver {
method SetMid (line 223) | func (t *RTPTransceiver) SetMid(mid string) error {
method Mid (line 233) | func (t *RTPTransceiver) Mid() string {
method Kind (line 242) | func (t *RTPTransceiver) Kind() RTPCodecType {
method Direction (line 247) | func (t *RTPTransceiver) Direction() RTPTransceiverDirection {
method Stop (line 256) | func (t *RTPTransceiver) Stop() error {
method setReceiver (line 274) | func (t *RTPTransceiver) setReceiver(r *RTPReceiver) {
method setDirection (line 286) | func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) {
method setCurrentDirection (line 290) | func (t *RTPTransceiver) setCurrentDirection(d RTPTransceiverDirection) {
method getCurrentDirection (line 294) | func (t *RTPTransceiver) getCurrentDirection() RTPTransceiverDirection {
method setCurrentRemoteDirection (line 302) | func (t *RTPTransceiver) setCurrentRemoteDirection(d RTPTransceiverDir...
method getCurrentRemoteDirection (line 306) | func (t *RTPTransceiver) getCurrentRemoteDirection() RTPTransceiverDir...
method setSendingTrack (line 314) | func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error { //n...
method isSendAllowed (line 344) | func (t *RTPTransceiver) isSendAllowed(kind RTPCodecType) bool {
function newRTPTransceiver (line 36) | func newRTPTransceiver(
function findByMid (line 375) | func findByMid(mid string, localTransceivers []*RTPTransceiver) (*RTPTra...
function satisfyTypeAndDirection (line 387) | func satisfyTypeAndDirection(
function handleUnknownRTPPacket (line 424) | func handleUnknownRTPPacket(
FILE: rtptransceiver_js.go
type RTPTransceiver (line 14) | type RTPTransceiver struct
method JSValue (line 20) | func (r *RTPTransceiver) JSValue() js.Value {
method Direction (line 25) | func (r *RTPTransceiver) Direction() RTPTransceiverDirection {
method Sender (line 30) | func (r *RTPTransceiver) Sender() *RTPSender {
method Receiver (line 40) | func (r *RTPTransceiver) Receiver() *RTPReceiver {
FILE: rtptransceiver_test.go
function Test_RTPTransceiver_SetCodecPreferences (line 15) | func Test_RTPTransceiver_SetCodecPreferences(t *testing.T) {
function Test_RTPTransceiver_SetCodecPreferences_PayloadType (line 89) | func Test_RTPTransceiver_SetCodecPreferences_PayloadType(t *testing.T) {
function Test_RTPTransceiver_UnattachedRTX (line 161) | func Test_RTPTransceiver_UnattachedRTX(t *testing.T) {
FILE: rtptransceiverdirection.go
type RTPTransceiverDirection (line 9) | type RTPTransceiverDirection
method String (line 57) | func (t RTPTransceiverDirection) String() string {
method Revers (line 73) | func (t RTPTransceiverDirection) Revers() RTPTransceiverDirection {
constant RTPTransceiverDirectionUnknown (line 13) | RTPTransceiverDirectionUnknown RTPTransceiverDirection = iota
constant RTPTransceiverDirectionSendrecv (line 17) | RTPTransceiverDirectionSendrecv
constant RTPTransceiverDirectionSendonly (line 21) | RTPTransceiverDirectionSendonly
constant RTPTransceiverDirectionRecvonly (line 25) | RTPTransceiverDirectionRecvonly
constant RTPTransceiverDirectionInactive (line 29) | RTPTransceiverDirectionInactive
constant rtpTransceiverDirectionSendrecvStr (line 34) | rtpTransceiverDirectionSendrecvStr = "sendrecv"
constant rtpTransceiverDirectionSendonlyStr (line 35) | rtpTransceiverDirectionSendonlyStr = "sendonly"
constant rtpTransceiverDirectionRecvonlyStr (line 36) | rtpTransceiverDirectionRecvonlyStr = "recvonly"
constant rtpTransceiverDirectionInactiveStr (line 37) | rtpTransceiverDirectionInactiveStr = "inactive"
function NewRTPTransceiverDirection (line 42) | func NewRTPTransceiverDirection(raw string) RTPTransceiverDirection {
function haveRTPTransceiverDirectionIntersection (line 84) | func haveRTPTransceiverDirectionIntersection(
FILE: rtptransceiverdirection_test.go
function TestNewRTPTransceiverDirection (line 12) | func TestNewRTPTransceiverDirection(t *testing.T) {
function TestRTPTransceiverDirection_String (line 33) | func TestRTPTransceiverDirection_String(t *testing.T) {
FILE: rtptransceiverinit.go
type RTPTransceiverInit (line 8) | type RTPTransceiverInit struct
FILE: rtptransceiverinit_go_test.go
function Test_RTPTransceiverInit_SSRC (line 17) | func Test_RTPTransceiverInit_SSRC(t *testing.T) {
FILE: sctpcapabilities.go
type SCTPCapabilities (line 7) | type SCTPCapabilities struct
FILE: sctptransport.go
constant sctpMaxChannels (line 20) | sctpMaxChannels = uint16(65535)
type SCTPTransport (line 23) | type SCTPTransport struct
method Transport (line 77) | func (r *SCTPTransport) Transport() *DTLSTransport {
method GetCapabilities (line 85) | func (r *SCTPTransport) GetCapabilities() SCTPCapabilities {
method Start (line 99) | func (r *SCTPTransport) Start(capabilities SCTPCapabilities) error {
method Stop (line 160) | func (r *SCTPTransport) Stop() error {
method acceptDataChannels (line 176) | func (r *SCTPTransport) acceptDataChannels(
method OnError (line 286) | func (r *SCTPTransport) OnError(f func(err error)) {
method onError (line 292) | func (r *SCTPTransport) onError(err error) {
method OnClose (line 303) | func (r *SCTPTransport) OnClose(f func(err error)) {
method onClose (line 309) | func (r *SCTPTransport) onClose(err error) {
method OnDataChannel (line 321) | func (r *SCTPTransport) OnDataChannel(f func(*DataChannel)) {
method OnDataChannelOpened (line 329) | func (r *SCTPTransport) OnDataChannelOpened(f func(*DataChannel)) {
method onDataChannel (line 335) | func (r *SCTPTransport) onDataChannel(dc *DataChannel) (done chan stru...
method updateMaxChannels (line 366) | func (r *SCTPTransport) updateMaxChannels() {
method MaxChannels (line 372) | func (r *SCTPTransport) MaxChannels() uint16 {
method State (line 384) | func (r *SCTPTransport) State() SCTPTransportState {
method Stats (line 392) | func (r *SCTPTransport) Stats() SCTPTransportStats {
method collectStats (line 412) | func (r *SCTPTransport) collectStats(collector *statsReportCollector) {
method generateAndSetDataChannelID (line 418) | func (r *SCTPTransport) generateAndSetDataChannelID(dtlsRole DTLSRole,...
method association (line 442) | func (r *SCTPTransport) association() *sctp.Association {
method BufferedAmount (line 454) | func (r *SCTPTransport) BufferedAmount() int {
method NewSCTPTransport (line 62) | func (api *API) NewSCTPTransport(dtls *DTLSTransport) *SCTPTransport {
FILE: sctptransport_js.go
type SCTPTransport (line 12) | type SCTPTransport struct
method JSValue (line 18) | func (r *SCTPTransport) JSValue() js.Value {
method Transport (line 23) | func (r *SCTPTransport) Transport() *DTLSTransport {
FILE: sctptransport_test.go
function TestGenerateDataChannelID (line 20) | func TestGenerateDataChannelID(t *testing.T) {
function TestSCTPTransportOnClose (line 64) | func TestSCTPTransportOnClose(t *testing.T) {
function TestSCTPTransportOnCloseImmediate (line 128) | func TestSCTPTransportOnCloseImmediate(t *testing.T) {
function TestSCTPTransportOutOfBandNegotiatedDataChannelDetach (line 189) | func TestSCTPTransportOutOfBandNegotiatedDataChannelDetach(t *testing.T)...
function TestMaxMessageSizeSignaling (line 298) | func TestMaxMessageSizeSignaling(t *testing.T) {
FILE: sctptransportstate.go
type SCTPTransportState (line 7) | type SCTPTransportState
method String (line 49) | func (s SCTPTransportState) String() string {
constant SCTPTransportStateUnknown (line 11) | SCTPTransportStateUnknown SCTPTransportState = iota
constant SCTPTransportStateConnecting (line 16) | SCTPTransportStateConnecting
constant SCTPTransportStateConnected (line 20) | SCTPTransportStateConnected
constant SCTPTransportStateClosed (line 26) | SCTPTransportStateClosed
constant sctpTransportStateConnectingStr (line 31) | sctpTransportStateConnectingStr = "connecting"
constant sctpTransportStateConnectedStr (line 32) | sctpTransportStateConnectedStr = "connected"
constant sctpTransportStateClosedStr (line 33) | sctpTransportStateClosedStr = "closed"
function newSCTPTransportState (line 36) | func newSCTPTransportState(raw string) SCTPTransportState {
FILE: sctptransportstate_test.go
function TestNewSCTPTransportState (line 12) | func TestNewSCTPTransportState(t *testing.T) {
function TestSCTPTransportState_String (line 32) | func TestSCTPTransportState_String(t *testing.T) {
FILE: sdp.go
type trackDetails (line 25) | type trackDetails struct
function trackDetailsForSSRC (line 36) | func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackD...
function trackDetailsForRID (line 46) | func trackDetailsForRID(trackDetails []trackDetails, mid, rid string) *t...
function filterTrackWithSSRC (line 60) | func filterTrackWithSSRC(incomingTracks []trackDetails, ssrc SSRC) []tra...
function trackDetailsFromSDP (line 78) | func trackDetailsFromSDP(
function trackDetailsToRTPReceiveParameters (line 260) | func trackDetailsToRTPReceiveParameters(trackDetails *trackDetails) RTPR...
function getRids (line 284) | func getRids(media *sdp.MediaDescription) []*simulcastRid {
function addCandidatesToMediaDescriptions (line 318) | func addCandidatesToMediaDescriptions(
function addDataMediaSection (line 361) | func addDataMediaSection(
function populateLocalCandidates (line 409) | func populateLocalCandidates(
function addSenderSDP (line 444) | func addSenderSDP(
function addTransceiverSDP (line 532) | func addTransceiverSDP(
type simulcastRid (line 661) | type simulcastRid struct
type mediaSection (line 667) | type mediaSection struct
function bundleMatchFromRemote (line 675) | func bundleMatchFromRemote(matchBundleGroup *string) func(mid string) bo...
function populateSDP (line 691) | func populateSDP(
function getMidValue (line 798) | func getMidValue(media *sdp.MediaDescription) string {
function descriptionIsPlanB (line 809) | func descriptionIsPlanB(desc *SessionDescription, log logging.LeveledLog...
function descriptionPossiblyPlanB (line 830) | func descriptionPossiblyPlanB(desc *SessionDescription) bool {
function getPeerDirection (line 845) | func getPeerDirection(media *sdp.MediaDescription) RTPTransceiverDirecti...
function extractBundleID (line 855) | func extractBundleID(desc *sdp.SessionDescription) string {
function extractFingerprint (line 873) | func extractFingerprint(desc *sdp.SessionDescription) (string, string, e...
type identifiedMediaDescription (line 920) | type identifiedMediaDescription struct
function extractICEDetailsFromMedia (line 926) | func extractICEDetailsFromMedia( //nolint:cyclop
type sdpICEDetails (line 994) | type sdpICEDetails struct
function extractICEDetails (line 1000) | func extractICEDetails(
function selectCandidateMediaSection (line 1043) | func selectCandidateMediaSection(sessionDescription *sdp.SessionDescript...
function getByMid (line 1073) | func getByMid(searchMid string, desc *SessionDescription) *sdp.MediaDesc...
function haveDataChannel (line 1084) | func haveDataChannel(desc *SessionDescription) *sdp.MediaDescription {
function codecsFromMediaDescription (line 1094) | func codecsFromMediaDescription(mediaDescr *sdp.MediaDescription) (out [...
function rtpExtensionsFromMediaDescription (line 1146) | func rtpExtensionsFromMediaDescription(m *sdp.MediaDescription) (map[str...
function updateSDPOrigin (line 1167) | func updateSDPOr
Condensed preview — 388 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,310K chars).
[
{
"path": ".codacy.yaml",
"chars": 146,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nexclude_paths:\n "
},
{
"path": ".eslintrc.json",
"chars": 30,
"preview": "{\n \"extends\": [\"standard\"]\n}\n"
},
{
"path": ".github/.ci.conf",
"chars": 217,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nGO_JS_WASM_EXEC=${PW"
},
{
"path": ".github/.gitignore",
"chars": 110,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\n.goassets\n"
},
{
"path": ".github/fetch-scripts.sh",
"chars": 896,
"preview": "#!/bin/sh\n\n#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n#"
},
{
"path": ".github/install-hooks.sh",
"chars": 594,
"preview": "#!/bin/sh\n\n#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n#"
},
{
"path": ".github/pion-gopher-webrtc.png.license",
"chars": 94,
"preview": "SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\nSPDX-License-Identifier: MIT"
},
{
"path": ".github/workflows/api.yaml",
"chars": 588,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/browser-e2e.yaml",
"chars": 473,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\nname: Browser E2E\non:"
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 720,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/examples-tests.yaml",
"chars": 414,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\nname: Examples Tests\n"
},
{
"path": ".github/workflows/fuzz.yaml",
"chars": 738,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/lint.yaml",
"chars": 589,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/release.yml",
"chars": 680,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/renovate-go-sum-fix.yaml",
"chars": 695,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/reuse.yml",
"chars": 617,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/standardjs.yaml",
"chars": 428,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\nname: StandardJS\non:\n"
},
{
"path": ".github/workflows/test.yaml",
"chars": 1751,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".github/workflows/tidy-check.yaml",
"chars": 706,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n# If this re"
},
{
"path": ".gitignore",
"chars": 410,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\n### JetBrains IDE ##"
},
{
"path": ".golangci.yml",
"chars": 8444,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nversion: \"2\"\nlinters"
},
{
"path": ".goreleaser.yml",
"chars": 121,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nbuilds:\n- skip: true"
},
{
"path": ".reuse/dep5",
"chars": 588,
"preview": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: Pion\nSource: https://github.co"
},
{
"path": "DESIGN.md",
"chars": 2564,
"preview": "<h1 align=\"center\">\n Design\n</h1>\nWebRTC is a powerful, but complicated technology you can build amazing things with, i"
},
{
"path": "LICENSE",
"chars": 1093,
"preview": "MIT License\n\nCopyright (c) 2026 The Pion community <https://pion.ly>\n\nPermission is hereby granted, free of charge, to a"
},
{
"path": "LICENSES/MIT.txt",
"chars": 1078,
"preview": "MIT License\n\nCopyright (c) <year> <copyright holders>\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "README.md",
"chars": 8848,
"preview": "<h1 align=\"center\">\n <a href=\"https://pion.ly\"><img src=\"./.github/pion-gopher-webrtc.png\" alt=\"Pion WebRTC\" height=\"25"
},
{
"path": "api.go",
"chars": 2791,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "api_js.go",
"chars": 781,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "api_test.go",
"chars": 1090,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "bundlepolicy.go",
"chars": 2322,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "bundlepolicy_test.go",
"chars": 1107,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "certificate.go",
"chars": 7608,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "certificate_js_test.go",
"chars": 4663,
"preview": "//go:build js && wasm\n// +build js,wasm\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-Li"
},
{
"path": "certificate_test.go",
"chars": 4638,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "codecov.yml",
"chars": 461,
"preview": "#\n# DO NOT EDIT THIS FILE\n#\n# It is automatically copied from https://github.com/pion/.goassets repository.\n#\n# SPDX-Fil"
},
{
"path": "configuration.go",
"chars": 2772,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "configuration_common.go",
"chars": 911,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "configuration_js.go",
"chars": 1524,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "configuration_test.go",
"chars": 1898,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "constants.go",
"chars": 2262,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "datachannel.go",
"chars": 20779,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "datachannel_go_test.go",
"chars": 23858,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "datachannel_js.go",
"chars": 12415,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "datachannel_js_detach.go",
"chars": 1481,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "datachannel_test.go",
"chars": 13358,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "datachannelinit.go",
"chars": 1451,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "datachannelmessage.go",
"chars": 388,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "datachannelparameters.go",
"chars": 551,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "datachannelstate.go",
"chars": 2294,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "datachannelstate_test.go",
"chars": 1206,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "dtlsfingerprint.go",
"chars": 671,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "dtlsparameters.go",
"chars": 317,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "dtlsrole.go",
"chars": 2534,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "dtlsrole_test.go",
"chars": 2036,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "dtlstransport.go",
"chars": 18737,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "dtlstransport_js.go",
"chars": 1643,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "dtlstransport_test.go",
"chars": 16163,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "dtlstransportstate.go",
"chars": 2735,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\n//"
},
{
"path": "dtlstransportstate_test.go",
"chars": 1320,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "e2e/Dockerfile",
"chars": 340,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nFROM golang:1.26-alp"
},
{
"path": "e2e/e2e_test.go",
"chars": 8839,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build e2e\n// "
},
{
"path": "e2e/test.html",
"chars": 1169,
"preview": "<!--\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n-->\n<div id=\"med"
},
{
"path": "errors.go",
"chars": 16946,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\npackage webrtc\n\nim"
},
{
"path": "examples/README.md",
"chars": 6241,
"preview": "<h1 align=\"center\">\n Examples\n</h1>\n\nWe've built an extensive collection of examples covering common use-cases. You can"
},
{
"path": "examples/bandwidth-estimation-from-disk/README.md",
"chars": 2109,
"preview": "# bandwidth-estimation-from-disk\nbandwidth-estimation-from-disk demonstrates how to use Pion's Bandwidth Estimation APIs"
},
{
"path": "examples/bandwidth-estimation-from-disk/main.go",
"chars": 8613,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/broadcast/README.md",
"chars": 1948,
"preview": "# broadcast\nbroadcast is a Pion WebRTC application that demonstrates how to broadcast a video to many peers, while only "
},
{
"path": "examples/broadcast/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/broadcast/jsfiddle/demo.details",
"chars": 199,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: broadcast\n"
},
{
"path": "examples/broadcast/jsfiddle/demo.html",
"chars": 913,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\n<div id=\"signa"
},
{
"path": "examples/broadcast/jsfiddle/demo.js",
"chars": 2216,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/broadcast/main.go",
"chars": 6929,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/custom-logger/README.md",
"chars": 1593,
"preview": "# custom-logger\n\n`custom-logger` is an example demonstrating how to override the default logging behavior of the [Pion W"
},
{
"path": "examples/custom-logger/main.go",
"chars": 6226,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/data-channels/README.md",
"chars": 3396,
"preview": "# data-channels\ndata-channels is Pion's sample WebRTC app that lets you send and receive DataChannel messages from a web"
},
{
"path": "examples/data-channels/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/data-channels/jsfiddle/demo.details",
"chars": 257,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: data-chann"
},
{
"path": "examples/data-channels/jsfiddle/demo.html",
"chars": 682,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser base64"
},
{
"path": "examples/data-channels/jsfiddle/demo.js",
"chars": 1775,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/data-channels/jsfiddle/main.go",
"chars": 4576,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "examples/data-channels/main.go",
"chars": 4766,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// data-channels i"
},
{
"path": "examples/data-channels-detach/README.md",
"chars": 723,
"preview": "# data-channels-detach\ndata-channels-detach is an example that shows how you can detach a data channel. This allows dire"
},
{
"path": "examples/data-channels-detach/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/data-channels-detach/jsfiddle/demo.html",
"chars": 598,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser base64"
},
{
"path": "examples/data-channels-detach/jsfiddle/main.go",
"chars": 5555,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build js && w"
},
{
"path": "examples/data-channels-detach/main.go",
"chars": 5659,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// data-channels-d"
},
{
"path": "examples/data-channels-detach-create/README.md",
"chars": 1312,
"preview": "# data-channels-detach-create\ndata-channels-detach is an example that shows how you can detach a data channel. This allo"
},
{
"path": "examples/data-channels-detach-create/main.go",
"chars": 5659,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// data-channels-d"
},
{
"path": "examples/data-channels-flow-control/README.md",
"chars": 2740,
"preview": "# data-channels-flow-control\nThis example demonstrates how to use the following property / methods.\n\n* func (d *DataChan"
},
{
"path": "examples/data-channels-flow-control/main.go",
"chars": 6332,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// data-channels-f"
},
{
"path": "examples/data-channels-simple/README.md",
"chars": 452,
"preview": "# WebRTC DataChannel Example in Go\n\nThis is a minimal example of a **WebRTC DataChannel** using **Go (Pion)** as the sig"
},
{
"path": "examples/data-channels-simple/demo.html",
"chars": 2376,
"preview": "<!DOCTYPE html>\n<html>\n <!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identif"
},
{
"path": "examples/data-channels-simple/main.go",
"chars": 3807,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/data-channels-whip-whep-like/README.md",
"chars": 2878,
"preview": "# whip-whep-like\n\nThis example demonstrates a WHIP/WHEP-like implementation using Pion WebRTC with DataChannel support f"
},
{
"path": "examples/data-channels-whip-whep-like/index.html",
"chars": 2658,
"preview": "<html>\n\n <!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n "
},
{
"path": "examples/data-channels-whip-whep-like/main.go",
"chars": 7394,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/example.html",
"chars": 1023,
"preview": "<html>\n\t<!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n\t<h"
},
{
"path": "examples/examples.go",
"chars": 3318,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// HTTP server tha"
},
{
"path": "examples/examples.json",
"chars": 5659,
"preview": "[\n\t{\n\t\t\"title\": \"Data Channels\",\n\t\t\"link\": \"data-channels\",\n\t\t\"description\": \"The data-channels example shows how you ca"
},
{
"path": "examples/ice-proxy/README.md",
"chars": 1522,
"preview": "# ICE Proxy\n`ice-proxy` demonstrates Pion WebRTC's capabilities for utilizing a proxy in WebRTC connections.\n\nThis proxy"
},
{
"path": "examples/ice-proxy/answer.go",
"chars": 3413,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "examples/ice-proxy/main.go",
"chars": 661,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/ice-proxy/offer.go",
"chars": 2810,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "examples/ice-proxy/proxy.go",
"chars": 2491,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "examples/ice-proxy/turn.go",
"chars": 811,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\npa"
},
{
"path": "examples/ice-restart/README.md",
"chars": 1061,
"preview": "# ice-restart\nice-restart demonstrates Pion WebRTC's ICE Restart abilities.\n\n## Instructions\n\n### Download ice-restart\nT"
},
{
"path": "examples/ice-restart/index.html",
"chars": 2282,
"preview": "<html>\n <!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n "
},
{
"path": "examples/ice-restart/main.go",
"chars": 2360,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// ice-restart dem"
},
{
"path": "examples/ice-single-port/README.md",
"chars": 1018,
"preview": "# ice-single-port\nice-single-port demonstrates Pion WebRTC's ability to serve many PeerConnections on a single port.\n\nPi"
},
{
"path": "examples/ice-single-port/index.html",
"chars": 1464,
"preview": "<html>\n <!--\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n -"
},
{
"path": "examples/ice-single-port/main.go",
"chars": 3090,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/ice-tcp/README.md",
"chars": 606,
"preview": "# ice-tcp\nice-tcp demonstrates Pion WebRTC's ICE TCP abilities.\n\n## Instructions\n\n### Download ice-tcp\nThis example requ"
},
{
"path": "examples/ice-tcp/index.html",
"chars": 1410,
"preview": "<html>\n <!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n "
},
{
"path": "examples/ice-tcp/main.go",
"chars": 2933,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/index.html",
"chars": 1463,
"preview": "<html>\n\t<!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n\t<h"
},
{
"path": "examples/insertable-streams/README.md",
"chars": 1790,
"preview": "# insertable-streams\ninsertable-streams demonstrates how to use insertable streams with Pion.\nThis example modifies the "
},
{
"path": "examples/insertable-streams/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/insertable-streams/jsfiddle/demo.details",
"chars": 252,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: play-from-"
},
{
"path": "examples/insertable-streams/jsfiddle/demo.html",
"chars": 767,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\n<div id=\"no-su"
},
{
"path": "examples/insertable-streams/jsfiddle/demo.js",
"chars": 3032,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/insertable-streams/main.go",
"chars": 6086,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/ortc/README.md",
"chars": 1356,
"preview": "# ortc\nortc demonstrates Pion WebRTC's [ORTC](https://ortc.org/) capabilities. Instead of using the Session Description "
},
{
"path": "examples/ortc/main.go",
"chars": 5908,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/ortc-media/README.md",
"chars": 2019,
"preview": "# ortc-media\nortc demonstrates Pion WebRTC's [ORTC](https://ortc.org/) capabilities. Instead of using the Session Descri"
},
{
"path": "examples/ortc-media/main.go",
"chars": 7006,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/pion-to-pion/README.md",
"chars": 669,
"preview": "# pion-to-pion\npion-to-pion is an example of two pion instances communicating directly!\n\nThe SDP offer and answer are ex"
},
{
"path": "examples/pion-to-pion/answer/Dockerfile",
"chars": 225,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nFROM golang:1.26\n\nRU"
},
{
"path": "examples/pion-to-pion/answer/main.go",
"chars": 6423,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// pion-to-pion is"
},
{
"path": "examples/pion-to-pion/docker-compose.yml",
"chars": 360,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\nversion: '3'\nservices"
},
{
"path": "examples/pion-to-pion/offer/Dockerfile",
"chars": 209,
"preview": "# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nFROM golang:1.26\n\nRU"
},
{
"path": "examples/pion-to-pion/offer/main.go",
"chars": 6368,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n// pion-to-pion is"
},
{
"path": "examples/pion-to-pion/test.sh",
"chars": 412,
"preview": "#!/bin/bash -eu\n\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\ndoc"
},
{
"path": "examples/play-from-disk/README.md",
"chars": 2207,
"preview": "# play-from-disk\nplay-from-disk demonstrates how to send video and/or audio to your browser from files saved to disk.\n\nF"
},
{
"path": "examples/play-from-disk/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/play-from-disk/jsfiddle/demo.details",
"chars": 252,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: play-from-"
},
{
"path": "examples/play-from-disk/jsfiddle/demo.html",
"chars": 588,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser Sessio"
},
{
"path": "examples/play-from-disk/jsfiddle/demo.js",
"chars": 1732,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/play-from-disk/main.go",
"chars": 9376,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/play-from-disk-fec/README.md",
"chars": 2599,
"preview": "# play-from-disk-fec\nplay-from-disk-fec demonstrates how to use forward error correction (FlexFEC-03) while sending vide"
},
{
"path": "examples/play-from-disk-fec/jsfiddle/demo.css",
"chars": 162,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/play-from-disk-fec/jsfiddle/demo.details",
"chars": 330,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: play-from-"
},
{
"path": "examples/play-from-disk-fec/jsfiddle/demo.html",
"chars": 651,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nRemote Session"
},
{
"path": "examples/play-from-disk-fec/jsfiddle/demo.js",
"chars": 1807,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/play-from-disk-fec/main.go",
"chars": 9303,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/play-from-disk-playlist-control/README.md",
"chars": 2157,
"preview": "# ogg-playlist-sctp\nStreams Opus pages from multi or single track Ogg containers, exposes the playlist over an SCTP Data"
},
{
"path": "examples/play-from-disk-playlist-control/main.go",
"chars": 13040,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/play-from-disk-playlist-control/web/app.css",
"chars": 1847,
"preview": "/* SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n * SPDX-License-Identifier: MIT\n */\n\nbody {\n font-"
},
{
"path": "examples/play-from-disk-playlist-control/web/app.js",
"chars": 5752,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/play-from-disk-playlist-control/web/index.html",
"chars": 1380,
"preview": "<!--\nSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\nSPDX-License-Identifier: MIT\n-->\n<!doctype html>\n"
},
{
"path": "examples/play-from-disk-renegotiation/README.md",
"chars": 1941,
"preview": "# play-from-disk-renegotiation\nplay-from-disk-renegotiation demonstrates Pion WebRTC's renegotiation abilities.\n\nFor a s"
},
{
"path": "examples/play-from-disk-renegotiation/index.html",
"chars": 1977,
"preview": "<html>\n <!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n "
},
{
"path": "examples/play-from-disk-renegotiation/main.go",
"chars": 6502,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/quick-switch/README.md",
"chars": 1329,
"preview": "# quick-switch\nquick-switch demonstrates how to quickly switch between multiple videos using WebRTC.\nSimiliar to how sit"
},
{
"path": "examples/quick-switch/index.html",
"chars": 1264,
"preview": "<html>\n<!--\n\t\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\t\tSPDX-License-Identifier: MIT\n\t-->\n\n<he"
},
{
"path": "examples/quick-switch/main.go",
"chars": 4503,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/reflect/README.md",
"chars": 1166,
"preview": "# reflect\nreflect demonstrates how with one PeerConnection you can send video to Pion and have the packets sent back. Th"
},
{
"path": "examples/reflect/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/reflect/jsfiddle/demo.details",
"chars": 262,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: reflect\nde"
},
{
"path": "examples/reflect/jsfiddle/demo.html",
"chars": 574,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser base64"
},
{
"path": "examples/reflect/jsfiddle/demo.js",
"chars": 1662,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/reflect/main.go",
"chars": 6657,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/repacketize/README.md",
"chars": 758,
"preview": "# repacketize\n\nrepacketize demonstrates how many video codecs can be received, depacketized and packetized by Pion over "
},
{
"path": "examples/repacketize/index.html",
"chars": 774,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\n<!DOCTYPE html"
},
{
"path": "examples/repacketize/index.js",
"chars": 2825,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/repacketize/main.go",
"chars": 5431,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/rtcp-processing/README.md",
"chars": 1625,
"preview": "# rtcp-processing\nrtcp-processing demonstrates the Public API for processing RTCP packets in Pion WebRTC.\n\nThis example "
},
{
"path": "examples/rtcp-processing/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/rtcp-processing/jsfiddle/demo.details",
"chars": 238,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: rtcp-proce"
},
{
"path": "examples/rtcp-processing/jsfiddle/demo.html",
"chars": 626,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser Sessio"
},
{
"path": "examples/rtcp-processing/jsfiddle/demo.js",
"chars": 1804,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/rtcp-processing/main.go",
"chars": 3478,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/rtp-forwarder/README.md",
"chars": 2026,
"preview": "# rtp-forwarder\nrtp-forwarder is a simple application that shows how to forward your webcam/microphone via RTP using Pio"
},
{
"path": "examples/rtp-forwarder/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/rtp-forwarder/jsfiddle/demo.details",
"chars": 228,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: rtp-forwar"
},
{
"path": "examples/rtp-forwarder/jsfiddle/demo.html",
"chars": 612,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser base64"
},
{
"path": "examples/rtp-forwarder/jsfiddle/demo.js",
"chars": 1496,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/rtp-forwarder/main.go",
"chars": 8840,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/rtp-forwarder/rtp-forwarder.sdp",
"chars": 164,
"preview": "v=0\no=- 0 0 IN IP4 127.0.0.1\ns=Pion WebRTC\nc=IN IP4 127.0.0.1\nt=0 0\nm=audio 4000 RTP/AVP 111\na=rtpmap:111 OPUS/48000/2\nm"
},
{
"path": "examples/rtp-forwarder/rtp-forwarder.sdp.license",
"chars": 94,
"preview": "SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\nSPDX-License-Identifier: MIT"
},
{
"path": "examples/rtp-to-webrtc/README.md",
"chars": 2823,
"preview": "# rtp-to-webrtc\nrtp-to-webrtc demonstrates how to consume a RTP stream video UDP, and then send to a WebRTC client.\n\nWit"
},
{
"path": "examples/rtp-to-webrtc/main.go",
"chars": 4308,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/save-to-disk/README.md",
"chars": 2019,
"preview": "# save-to-disk\nsave-to-disk is a simple application that shows how to record your webcam/microphone using Pion WebRTC an"
},
{
"path": "examples/save-to-disk/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/save-to-disk/jsfiddle/demo.details",
"chars": 232,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: save-to-di"
},
{
"path": "examples/save-to-disk/jsfiddle/demo.html",
"chars": 612,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\nBrowser base64"
},
{
"path": "examples/save-to-disk/jsfiddle/demo.js",
"chars": 1457,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/save-to-disk/main.go",
"chars": 7211,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/save-to-disk-av1/README.md",
"chars": 1834,
"preview": "# save-to-disk-av1\nsave-to-disk-av1 is a simple application that shows how to save a video to disk using AV1.\n\nIf you wi"
},
{
"path": "examples/save-to-disk-av1/main.go",
"chars": 6140,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/simulcast/README.md",
"chars": 1411,
"preview": "# simulcast\ndemonstrates of how to handle incoming track with multiple simulcast rtp streams and show all them back.\n\nTh"
},
{
"path": "examples/simulcast/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
},
{
"path": "examples/simulcast/jsfiddle/demo.details",
"chars": 269,
"preview": "---\n# SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n# SPDX-License-Identifier: MIT\n\nname: simulcast\n"
},
{
"path": "examples/simulcast/jsfiddle/demo.html",
"chars": 738,
"preview": "<!--\n\tSPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n\tSPDX-License-Identifier: MIT\n-->\n<script src=\"h"
},
{
"path": "examples/simulcast/jsfiddle/demo.js",
"chars": 2629,
"preview": "/* eslint-env browser */\n\n// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifie"
},
{
"path": "examples/simulcast/main.go",
"chars": 6677,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/stats/README.md",
"chars": 1925,
"preview": "# stats\nstats demonstrates how to use the [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) implementation provided by"
},
{
"path": "examples/stats/main.go",
"chars": 5719,
"preview": "// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n// SPDX-License-Identifier: MIT\n\n//go:build !js\n\n//"
},
{
"path": "examples/swap-tracks/README.md",
"chars": 1174,
"preview": "# swap-tracks\nswap-tracks demonstrates how to swap multiple incoming tracks on a single outgoing track.\n\n## Instructions"
},
{
"path": "examples/swap-tracks/jsfiddle/demo.css",
"chars": 161,
"preview": "/*\n SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>\n SPDX-License-Identifier: MIT\n*/\ntextarea {\n"
}
]
// ... and 188 more files (download for full content)
About this extraction
This page contains the full source code of the pion/webrtc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 388 files (2.0 MB), approximately 545.1k tokens, and a symbol index with 2683 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.