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