icecandidate.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package webrtc
  4. import (
  5. "fmt"
  6. "github.com/pion/ice/v2"
  7. )
  8. // ICECandidate represents a ice candidate
  9. type ICECandidate struct {
  10. statsID string
  11. Foundation string `json:"foundation"`
  12. Priority uint32 `json:"priority"`
  13. Address string `json:"address"`
  14. Protocol ICEProtocol `json:"protocol"`
  15. Port uint16 `json:"port"`
  16. Typ ICECandidateType `json:"type"`
  17. Component uint16 `json:"component"`
  18. RelatedAddress string `json:"relatedAddress"`
  19. RelatedPort uint16 `json:"relatedPort"`
  20. TCPType string `json:"tcpType"`
  21. }
  22. // Conversion for package ice
  23. func newICECandidatesFromICE(iceCandidates []ice.Candidate) ([]ICECandidate, error) {
  24. candidates := []ICECandidate{}
  25. for _, i := range iceCandidates {
  26. c, err := newICECandidateFromICE(i)
  27. if err != nil {
  28. return nil, err
  29. }
  30. candidates = append(candidates, c)
  31. }
  32. return candidates, nil
  33. }
  34. func newICECandidateFromICE(i ice.Candidate) (ICECandidate, error) {
  35. typ, err := convertTypeFromICE(i.Type())
  36. if err != nil {
  37. return ICECandidate{}, err
  38. }
  39. protocol, err := NewICEProtocol(i.NetworkType().NetworkShort())
  40. if err != nil {
  41. return ICECandidate{}, err
  42. }
  43. c := ICECandidate{
  44. statsID: i.ID(),
  45. Foundation: i.Foundation(),
  46. Priority: i.Priority(),
  47. Address: i.Address(),
  48. Protocol: protocol,
  49. Port: uint16(i.Port()),
  50. Component: i.Component(),
  51. Typ: typ,
  52. TCPType: i.TCPType().String(),
  53. }
  54. if i.RelatedAddress() != nil {
  55. c.RelatedAddress = i.RelatedAddress().Address
  56. c.RelatedPort = uint16(i.RelatedAddress().Port)
  57. }
  58. return c, nil
  59. }
  60. func (c ICECandidate) toICE() (ice.Candidate, error) {
  61. candidateID := c.statsID
  62. switch c.Typ {
  63. case ICECandidateTypeHost:
  64. config := ice.CandidateHostConfig{
  65. CandidateID: candidateID,
  66. Network: c.Protocol.String(),
  67. Address: c.Address,
  68. Port: int(c.Port),
  69. Component: c.Component,
  70. TCPType: ice.NewTCPType(c.TCPType),
  71. Foundation: c.Foundation,
  72. Priority: c.Priority,
  73. }
  74. return ice.NewCandidateHost(&config)
  75. case ICECandidateTypeSrflx:
  76. config := ice.CandidateServerReflexiveConfig{
  77. CandidateID: candidateID,
  78. Network: c.Protocol.String(),
  79. Address: c.Address,
  80. Port: int(c.Port),
  81. Component: c.Component,
  82. Foundation: c.Foundation,
  83. Priority: c.Priority,
  84. RelAddr: c.RelatedAddress,
  85. RelPort: int(c.RelatedPort),
  86. }
  87. return ice.NewCandidateServerReflexive(&config)
  88. case ICECandidateTypePrflx:
  89. config := ice.CandidatePeerReflexiveConfig{
  90. CandidateID: candidateID,
  91. Network: c.Protocol.String(),
  92. Address: c.Address,
  93. Port: int(c.Port),
  94. Component: c.Component,
  95. Foundation: c.Foundation,
  96. Priority: c.Priority,
  97. RelAddr: c.RelatedAddress,
  98. RelPort: int(c.RelatedPort),
  99. }
  100. return ice.NewCandidatePeerReflexive(&config)
  101. case ICECandidateTypeRelay:
  102. config := ice.CandidateRelayConfig{
  103. CandidateID: candidateID,
  104. Network: c.Protocol.String(),
  105. Address: c.Address,
  106. Port: int(c.Port),
  107. Component: c.Component,
  108. Foundation: c.Foundation,
  109. Priority: c.Priority,
  110. RelAddr: c.RelatedAddress,
  111. RelPort: int(c.RelatedPort),
  112. }
  113. return ice.NewCandidateRelay(&config)
  114. default:
  115. return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ)
  116. }
  117. }
  118. func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
  119. switch t {
  120. case ice.CandidateTypeHost:
  121. return ICECandidateTypeHost, nil
  122. case ice.CandidateTypeServerReflexive:
  123. return ICECandidateTypeSrflx, nil
  124. case ice.CandidateTypePeerReflexive:
  125. return ICECandidateTypePrflx, nil
  126. case ice.CandidateTypeRelay:
  127. return ICECandidateTypeRelay, nil
  128. default:
  129. return ICECandidateType(t), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, t)
  130. }
  131. }
  132. func (c ICECandidate) String() string {
  133. ic, err := c.toICE()
  134. if err != nil {
  135. return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err)
  136. }
  137. return ic.String()
  138. }
  139. // ToJSON returns an ICECandidateInit
  140. // as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
  141. func (c ICECandidate) ToJSON() ICECandidateInit {
  142. zeroVal := uint16(0)
  143. emptyStr := ""
  144. candidateStr := ""
  145. candidate, err := c.toICE()
  146. if err == nil {
  147. candidateStr = candidate.Marshal()
  148. }
  149. return ICECandidateInit{
  150. Candidate: fmt.Sprintf("candidate:%s", candidateStr),
  151. SDPMid: &emptyStr,
  152. SDPMLineIndex: &zeroVal,
  153. }
  154. }