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  }