github.com/pion/webrtc/v4@v4.0.1/signalingstate.go (about) 1 // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly> 2 // SPDX-License-Identifier: MIT 3 4 package webrtc 5 6 import ( 7 "fmt" 8 "sync/atomic" 9 10 "github.com/pion/webrtc/v4/pkg/rtcerr" 11 ) 12 13 type stateChangeOp int 14 15 const ( 16 stateChangeOpSetLocal stateChangeOp = iota + 1 17 stateChangeOpSetRemote 18 ) 19 20 func (op stateChangeOp) String() string { 21 switch op { 22 case stateChangeOpSetLocal: 23 return "SetLocal" 24 case stateChangeOpSetRemote: 25 return "SetRemote" 26 default: 27 return "Unknown State Change Operation" 28 } 29 } 30 31 // SignalingState indicates the signaling state of the offer/answer process. 32 type SignalingState int32 33 34 const ( 35 // SignalingStateUnknown is the enum's zero-value 36 SignalingStateUnknown SignalingState = iota 37 38 // SignalingStateStable indicates there is no offer/answer exchange in 39 // progress. This is also the initial state, in which case the local and 40 // remote descriptions are nil. 41 SignalingStateStable 42 43 // SignalingStateHaveLocalOffer indicates that a local description, of 44 // type "offer", has been successfully applied. 45 SignalingStateHaveLocalOffer 46 47 // SignalingStateHaveRemoteOffer indicates that a remote description, of 48 // type "offer", has been successfully applied. 49 SignalingStateHaveRemoteOffer 50 51 // SignalingStateHaveLocalPranswer indicates that a remote description 52 // of type "offer" has been successfully applied and a local description 53 // of type "pranswer" has been successfully applied. 54 SignalingStateHaveLocalPranswer 55 56 // SignalingStateHaveRemotePranswer indicates that a local description 57 // of type "offer" has been successfully applied and a remote description 58 // of type "pranswer" has been successfully applied. 59 SignalingStateHaveRemotePranswer 60 61 // SignalingStateClosed indicates The PeerConnection has been closed. 62 SignalingStateClosed 63 ) 64 65 // This is done this way because of a linter. 66 const ( 67 signalingStateStableStr = "stable" 68 signalingStateHaveLocalOfferStr = "have-local-offer" 69 signalingStateHaveRemoteOfferStr = "have-remote-offer" 70 signalingStateHaveLocalPranswerStr = "have-local-pranswer" 71 signalingStateHaveRemotePranswerStr = "have-remote-pranswer" 72 signalingStateClosedStr = "closed" 73 ) 74 75 func newSignalingState(raw string) SignalingState { 76 switch raw { 77 case signalingStateStableStr: 78 return SignalingStateStable 79 case signalingStateHaveLocalOfferStr: 80 return SignalingStateHaveLocalOffer 81 case signalingStateHaveRemoteOfferStr: 82 return SignalingStateHaveRemoteOffer 83 case signalingStateHaveLocalPranswerStr: 84 return SignalingStateHaveLocalPranswer 85 case signalingStateHaveRemotePranswerStr: 86 return SignalingStateHaveRemotePranswer 87 case signalingStateClosedStr: 88 return SignalingStateClosed 89 default: 90 return SignalingStateUnknown 91 } 92 } 93 94 func (t SignalingState) String() string { 95 switch t { 96 case SignalingStateStable: 97 return signalingStateStableStr 98 case SignalingStateHaveLocalOffer: 99 return signalingStateHaveLocalOfferStr 100 case SignalingStateHaveRemoteOffer: 101 return signalingStateHaveRemoteOfferStr 102 case SignalingStateHaveLocalPranswer: 103 return signalingStateHaveLocalPranswerStr 104 case SignalingStateHaveRemotePranswer: 105 return signalingStateHaveRemotePranswerStr 106 case SignalingStateClosed: 107 return signalingStateClosedStr 108 default: 109 return ErrUnknownType.Error() 110 } 111 } 112 113 // Get thread safe read value 114 func (t *SignalingState) Get() SignalingState { 115 return SignalingState(atomic.LoadInt32((*int32)(t))) 116 } 117 118 // Set thread safe write value 119 func (t *SignalingState) Set(state SignalingState) { 120 atomic.StoreInt32((*int32)(t), int32(state)) 121 } 122 123 func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType SDPType) (SignalingState, error) { // nolint:gocognit 124 // Special case for rollbacks 125 if sdpType == SDPTypeRollback && cur == SignalingStateStable { 126 return cur, &rtcerr.InvalidModificationError{ 127 Err: errSignalingStateCannotRollback, 128 } 129 } 130 131 // 4.3.1 valid state transitions 132 switch cur { // nolint:exhaustive 133 case SignalingStateStable: 134 switch op { 135 case stateChangeOpSetLocal: 136 // stable->SetLocal(offer)->have-local-offer 137 if sdpType == SDPTypeOffer && next == SignalingStateHaveLocalOffer { 138 return next, nil 139 } 140 case stateChangeOpSetRemote: 141 // stable->SetRemote(offer)->have-remote-offer 142 if sdpType == SDPTypeOffer && next == SignalingStateHaveRemoteOffer { 143 return next, nil 144 } 145 } 146 case SignalingStateHaveLocalOffer: 147 if op == stateChangeOpSetRemote { 148 switch sdpType { // nolint:exhaustive 149 // have-local-offer->SetRemote(answer)->stable 150 case SDPTypeAnswer: 151 if next == SignalingStateStable { 152 return next, nil 153 } 154 // have-local-offer->SetRemote(pranswer)->have-remote-pranswer 155 case SDPTypePranswer: 156 if next == SignalingStateHaveRemotePranswer { 157 return next, nil 158 } 159 } 160 } 161 case SignalingStateHaveRemotePranswer: 162 if op == stateChangeOpSetRemote && sdpType == SDPTypeAnswer { 163 // have-remote-pranswer->SetRemote(answer)->stable 164 if next == SignalingStateStable { 165 return next, nil 166 } 167 } 168 case SignalingStateHaveRemoteOffer: 169 if op == stateChangeOpSetLocal { 170 switch sdpType { // nolint:exhaustive 171 // have-remote-offer->SetLocal(answer)->stable 172 case SDPTypeAnswer: 173 if next == SignalingStateStable { 174 return next, nil 175 } 176 // have-remote-offer->SetLocal(pranswer)->have-local-pranswer 177 case SDPTypePranswer: 178 if next == SignalingStateHaveLocalPranswer { 179 return next, nil 180 } 181 } 182 } 183 case SignalingStateHaveLocalPranswer: 184 if op == stateChangeOpSetLocal && sdpType == SDPTypeAnswer { 185 // have-local-pranswer->SetLocal(answer)->stable 186 if next == SignalingStateStable { 187 return next, nil 188 } 189 } 190 } 191 return cur, &rtcerr.InvalidModificationError{ 192 Err: fmt.Errorf("%w: %s->%s(%s)->%s", errSignalingStateProposedTransitionInvalid, cur, op, sdpType, next), 193 } 194 }