Files
upstream-pion-webrtc/signalingstate.go
upstream-mirror 8acb1a3a29
Some checks failed
REUSE Compliance Check / lint (push) Failing after 0s
Browser E2E / Test (push) Failing after 3s
Test / test (1.24) (push) Failing after 0s
Test / test-i386 (1.25) (push) Failing after 0s
Test / test-i386 (1.24) (push) Failing after 0s
Test / test-windows (1.25) (push) Failing after 0s
Test / test-windows (1.24) (push) Failing after 0s
Test / test (1.25) (push) Failing after 0s
Examples Tests / Test (push) Failing after 3s
Test / test-macos (1.25) (push) Failing after 0s
Test / test-macos (1.24) (push) Failing after 0s
Go mod tidy / tidy (push) Failing after 0s
Test / test-wasm (push) Failing after 0s
CodeQL / analyze (push) Failing after 1s
Fuzz / fuzz (push) Failing after 0s
snapshot of https://github.com/pion/webrtc.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 20:06:07 +05:00

197 lines
5.7 KiB
Go

// SPDX-FileCopyrightText: 2026 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package webrtc
import (
"fmt"
"sync/atomic"
"github.com/pion/webrtc/v4/pkg/rtcerr"
)
type stateChangeOp int
const (
stateChangeOpSetLocal stateChangeOp = iota + 1
stateChangeOpSetRemote
)
func (op stateChangeOp) String() string {
switch op {
case stateChangeOpSetLocal:
return "SetLocal"
case stateChangeOpSetRemote:
return "SetRemote"
default:
return "Unknown State Change Operation"
}
}
// SignalingState indicates the signaling state of the offer/answer process.
type SignalingState int32
const (
// SignalingStateUnknown is the enum's zero-value.
SignalingStateUnknown SignalingState = iota
// SignalingStateStable indicates there is no offer/answer exchange in
// progress. This is also the initial state, in which case the local and
// remote descriptions are nil.
SignalingStateStable
// SignalingStateHaveLocalOffer indicates that a local description, of
// type "offer", has been successfully applied.
SignalingStateHaveLocalOffer
// SignalingStateHaveRemoteOffer indicates that a remote description, of
// type "offer", has been successfully applied.
SignalingStateHaveRemoteOffer
// SignalingStateHaveLocalPranswer indicates that a remote description
// of type "offer" has been successfully applied and a local description
// of type "pranswer" has been successfully applied.
SignalingStateHaveLocalPranswer
// SignalingStateHaveRemotePranswer indicates that a local description
// of type "offer" has been successfully applied and a remote description
// of type "pranswer" has been successfully applied.
SignalingStateHaveRemotePranswer
// SignalingStateClosed indicates The PeerConnection has been closed.
SignalingStateClosed
)
// This is done this way because of a linter.
const (
signalingStateStableStr = "stable"
signalingStateHaveLocalOfferStr = "have-local-offer"
signalingStateHaveRemoteOfferStr = "have-remote-offer"
signalingStateHaveLocalPranswerStr = "have-local-pranswer"
signalingStateHaveRemotePranswerStr = "have-remote-pranswer"
signalingStateClosedStr = "closed"
)
func newSignalingState(raw string) SignalingState {
switch raw {
case signalingStateStableStr:
return SignalingStateStable
case signalingStateHaveLocalOfferStr:
return SignalingStateHaveLocalOffer
case signalingStateHaveRemoteOfferStr:
return SignalingStateHaveRemoteOffer
case signalingStateHaveLocalPranswerStr:
return SignalingStateHaveLocalPranswer
case signalingStateHaveRemotePranswerStr:
return SignalingStateHaveRemotePranswer
case signalingStateClosedStr:
return SignalingStateClosed
default:
return SignalingStateUnknown
}
}
func (t SignalingState) String() string {
switch t {
case SignalingStateStable:
return signalingStateStableStr
case SignalingStateHaveLocalOffer:
return signalingStateHaveLocalOfferStr
case SignalingStateHaveRemoteOffer:
return signalingStateHaveRemoteOfferStr
case SignalingStateHaveLocalPranswer:
return signalingStateHaveLocalPranswerStr
case SignalingStateHaveRemotePranswer:
return signalingStateHaveRemotePranswerStr
case SignalingStateClosed:
return signalingStateClosedStr
default:
return ErrUnknownType.Error()
}
}
// Get thread safe read value.
func (t *SignalingState) Get() SignalingState {
return SignalingState(atomic.LoadInt32((*int32)(t)))
}
// Set thread safe write value.
func (t *SignalingState) Set(state SignalingState) {
atomic.StoreInt32((*int32)(t), int32(state))
}
//nolint:gocognit,cyclop
func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType SDPType) (SignalingState, error) {
// Special case for rollbacks
if sdpType == SDPTypeRollback && cur == SignalingStateStable {
return cur, &rtcerr.InvalidModificationError{
Err: errSignalingStateCannotRollback,
}
}
// 4.3.1 valid state transitions
switch cur { // nolint:exhaustive
case SignalingStateStable:
switch op {
case stateChangeOpSetLocal:
// stable->SetLocal(offer)->have-local-offer
if sdpType == SDPTypeOffer && next == SignalingStateHaveLocalOffer {
return next, nil
}
case stateChangeOpSetRemote:
// stable->SetRemote(offer)->have-remote-offer
if sdpType == SDPTypeOffer && next == SignalingStateHaveRemoteOffer {
return next, nil
}
}
case SignalingStateHaveLocalOffer:
if op == stateChangeOpSetRemote {
switch sdpType { // nolint:exhaustive
// have-local-offer->SetRemote(answer)->stable
case SDPTypeAnswer:
if next == SignalingStateStable {
return next, nil
}
// have-local-offer->SetRemote(pranswer)->have-remote-pranswer
case SDPTypePranswer:
if next == SignalingStateHaveRemotePranswer {
return next, nil
}
}
}
case SignalingStateHaveRemotePranswer:
if op == stateChangeOpSetRemote && sdpType == SDPTypeAnswer {
// have-remote-pranswer->SetRemote(answer)->stable
if next == SignalingStateStable {
return next, nil
}
}
case SignalingStateHaveRemoteOffer:
if op == stateChangeOpSetLocal {
switch sdpType { // nolint:exhaustive
// have-remote-offer->SetLocal(answer)->stable
case SDPTypeAnswer:
if next == SignalingStateStable {
return next, nil
}
// have-remote-offer->SetLocal(pranswer)->have-local-pranswer
case SDPTypePranswer:
if next == SignalingStateHaveLocalPranswer {
return next, nil
}
}
}
case SignalingStateHaveLocalPranswer:
if op == stateChangeOpSetLocal && sdpType == SDPTypeAnswer {
// have-local-pranswer->SetLocal(answer)->stable
if next == SignalingStateStable {
return next, nil
}
}
}
return cur, &rtcerr.InvalidModificationError{
Err: fmt.Errorf("%w: %s->%s(%s)->%s", errSignalingStateProposedTransitionInvalid, cur, op, sdpType, next),
}
}