signalingstate.go 5.6 KB

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