peerconnection_js.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. //go:build js && wasm
  4. // +build js,wasm
  5. // Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document.
  6. package webrtc
  7. import (
  8. "syscall/js"
  9. "github.com/pion/ice/v2"
  10. "github.com/pion/webrtc/v3/pkg/rtcerr"
  11. )
  12. // PeerConnection represents a WebRTC connection that establishes a
  13. // peer-to-peer communications with another PeerConnection instance in a
  14. // browser, or to another endpoint implementing the required protocols.
  15. type PeerConnection struct {
  16. // Pointer to the underlying JavaScript RTCPeerConnection object.
  17. underlying js.Value
  18. // Keep track of handlers/callbacks so we can call Release as required by the
  19. // syscall/js API. Initially nil.
  20. onSignalingStateChangeHandler *js.Func
  21. onDataChannelHandler *js.Func
  22. onNegotiationNeededHandler *js.Func
  23. onConnectionStateChangeHandler *js.Func
  24. onICEConnectionStateChangeHandler *js.Func
  25. onICECandidateHandler *js.Func
  26. onICEGatheringStateChangeHandler *js.Func
  27. // Used by GatheringCompletePromise
  28. onGatherCompleteHandler func()
  29. // A reference to the associated API state used by this connection
  30. api *API
  31. }
  32. // NewPeerConnection creates a peerconnection.
  33. func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
  34. api := NewAPI()
  35. return api.NewPeerConnection(configuration)
  36. }
  37. // NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
  38. func (api *API) NewPeerConnection(configuration Configuration) (_ *PeerConnection, err error) {
  39. defer func() {
  40. if e := recover(); e != nil {
  41. err = recoveryToError(e)
  42. }
  43. }()
  44. configMap := configurationToValue(configuration)
  45. underlying := js.Global().Get("window").Get("RTCPeerConnection").New(configMap)
  46. return &PeerConnection{
  47. underlying: underlying,
  48. api: api,
  49. }, nil
  50. }
  51. // JSValue returns the underlying PeerConnection
  52. func (pc *PeerConnection) JSValue() js.Value {
  53. return pc.underlying
  54. }
  55. // OnSignalingStateChange sets an event handler which is invoked when the
  56. // peer connection's signaling state changes
  57. func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) {
  58. if pc.onSignalingStateChangeHandler != nil {
  59. oldHandler := pc.onSignalingStateChangeHandler
  60. defer oldHandler.Release()
  61. }
  62. onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  63. state := newSignalingState(args[0].String())
  64. go f(state)
  65. return js.Undefined()
  66. })
  67. pc.onSignalingStateChangeHandler = &onSignalingStateChangeHandler
  68. pc.underlying.Set("onsignalingstatechange", onSignalingStateChangeHandler)
  69. }
  70. // OnDataChannel sets an event handler which is invoked when a data
  71. // channel message arrives from a remote peer.
  72. func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
  73. if pc.onDataChannelHandler != nil {
  74. oldHandler := pc.onDataChannelHandler
  75. defer oldHandler.Release()
  76. }
  77. onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  78. // pion/webrtc/projects/15
  79. // This reference to the underlying DataChannel doesn't know
  80. // about any other references to the same DataChannel. This might result in
  81. // memory leaks where we don't clean up handler functions. Could possibly fix
  82. // by keeping a mutex-protected list of all DataChannel references as a
  83. // property of this PeerConnection, but at the cost of additional overhead.
  84. dataChannel := &DataChannel{
  85. underlying: args[0].Get("channel"),
  86. api: pc.api,
  87. }
  88. go f(dataChannel)
  89. return js.Undefined()
  90. })
  91. pc.onDataChannelHandler = &onDataChannelHandler
  92. pc.underlying.Set("ondatachannel", onDataChannelHandler)
  93. }
  94. // OnNegotiationNeeded sets an event handler which is invoked when
  95. // a change has occurred which requires session negotiation
  96. func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
  97. if pc.onNegotiationNeededHandler != nil {
  98. oldHandler := pc.onNegotiationNeededHandler
  99. defer oldHandler.Release()
  100. }
  101. onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  102. go f()
  103. return js.Undefined()
  104. })
  105. pc.onNegotiationNeededHandler = &onNegotiationNeededHandler
  106. pc.underlying.Set("onnegotiationneeded", onNegotiationNeededHandler)
  107. }
  108. // OnICEConnectionStateChange sets an event handler which is called
  109. // when an ICE connection state is changed.
  110. func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
  111. if pc.onICEConnectionStateChangeHandler != nil {
  112. oldHandler := pc.onICEConnectionStateChangeHandler
  113. defer oldHandler.Release()
  114. }
  115. onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  116. connectionState := NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
  117. go f(connectionState)
  118. return js.Undefined()
  119. })
  120. pc.onICEConnectionStateChangeHandler = &onICEConnectionStateChangeHandler
  121. pc.underlying.Set("oniceconnectionstatechange", onICEConnectionStateChangeHandler)
  122. }
  123. // OnConnectionStateChange sets an event handler which is called
  124. // when an PeerConnectionState is changed.
  125. func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
  126. if pc.onConnectionStateChangeHandler != nil {
  127. oldHandler := pc.onConnectionStateChangeHandler
  128. defer oldHandler.Release()
  129. }
  130. onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  131. connectionState := newPeerConnectionState(pc.underlying.Get("connectionState").String())
  132. go f(connectionState)
  133. return js.Undefined()
  134. })
  135. pc.onConnectionStateChangeHandler = &onConnectionStateChangeHandler
  136. pc.underlying.Set("onconnectionstatechange", onConnectionStateChangeHandler)
  137. }
  138. func (pc *PeerConnection) checkConfiguration(configuration Configuration) error {
  139. // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
  140. if pc.ConnectionState() == PeerConnectionStateClosed {
  141. return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
  142. }
  143. existingConfig := pc.GetConfiguration()
  144. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
  145. if configuration.PeerIdentity != "" {
  146. if configuration.PeerIdentity != existingConfig.PeerIdentity {
  147. return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
  148. }
  149. }
  150. // https://github.com/pion/webrtc/issues/513
  151. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
  152. // if len(configuration.Certificates) > 0 {
  153. // if len(configuration.Certificates) != len(existingConfiguration.Certificates) {
  154. // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
  155. // }
  156. // for i, certificate := range configuration.Certificates {
  157. // if !pc.configuration.Certificates[i].Equals(certificate) {
  158. // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
  159. // }
  160. // }
  161. // pc.configuration.Certificates = configuration.Certificates
  162. // }
  163. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
  164. if configuration.BundlePolicy != BundlePolicy(Unknown) {
  165. if configuration.BundlePolicy != existingConfig.BundlePolicy {
  166. return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
  167. }
  168. }
  169. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
  170. if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
  171. if configuration.RTCPMuxPolicy != existingConfig.RTCPMuxPolicy {
  172. return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
  173. }
  174. }
  175. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
  176. if configuration.ICECandidatePoolSize != 0 {
  177. if configuration.ICECandidatePoolSize != existingConfig.ICECandidatePoolSize &&
  178. pc.LocalDescription() != nil {
  179. return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
  180. }
  181. }
  182. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
  183. if len(configuration.ICEServers) > 0 {
  184. // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
  185. for _, server := range configuration.ICEServers {
  186. if _, err := server.validate(); err != nil {
  187. return err
  188. }
  189. }
  190. }
  191. return nil
  192. }
  193. // SetConfiguration updates the configuration of this PeerConnection object.
  194. func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err error) {
  195. defer func() {
  196. if e := recover(); e != nil {
  197. err = recoveryToError(e)
  198. }
  199. }()
  200. if err := pc.checkConfiguration(configuration); err != nil {
  201. return err
  202. }
  203. configMap := configurationToValue(configuration)
  204. pc.underlying.Call("setConfiguration", configMap)
  205. return nil
  206. }
  207. // GetConfiguration returns a Configuration object representing the current
  208. // configuration of this PeerConnection object. The returned object is a
  209. // copy and direct mutation on it will not take affect until SetConfiguration
  210. // has been called with Configuration passed as its only argument.
  211. // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration
  212. func (pc *PeerConnection) GetConfiguration() Configuration {
  213. return valueToConfiguration(pc.underlying.Call("getConfiguration"))
  214. }
  215. // CreateOffer starts the PeerConnection and generates the localDescription
  216. func (pc *PeerConnection) CreateOffer(options *OfferOptions) (_ SessionDescription, err error) {
  217. defer func() {
  218. if e := recover(); e != nil {
  219. err = recoveryToError(e)
  220. }
  221. }()
  222. promise := pc.underlying.Call("createOffer", offerOptionsToValue(options))
  223. desc, err := awaitPromise(promise)
  224. if err != nil {
  225. return SessionDescription{}, err
  226. }
  227. return *valueToSessionDescription(desc), nil
  228. }
  229. // CreateAnswer starts the PeerConnection and generates the localDescription
  230. func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (_ SessionDescription, err error) {
  231. defer func() {
  232. if e := recover(); e != nil {
  233. err = recoveryToError(e)
  234. }
  235. }()
  236. promise := pc.underlying.Call("createAnswer", answerOptionsToValue(options))
  237. desc, err := awaitPromise(promise)
  238. if err != nil {
  239. return SessionDescription{}, err
  240. }
  241. return *valueToSessionDescription(desc), nil
  242. }
  243. // SetLocalDescription sets the SessionDescription of the local peer
  244. func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) (err error) {
  245. defer func() {
  246. if e := recover(); e != nil {
  247. err = recoveryToError(e)
  248. }
  249. }()
  250. promise := pc.underlying.Call("setLocalDescription", sessionDescriptionToValue(&desc))
  251. _, err = awaitPromise(promise)
  252. return err
  253. }
  254. // LocalDescription returns PendingLocalDescription if it is not null and
  255. // otherwise it returns CurrentLocalDescription. This property is used to
  256. // determine if setLocalDescription has already been called.
  257. // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription
  258. func (pc *PeerConnection) LocalDescription() *SessionDescription {
  259. return valueToSessionDescription(pc.underlying.Get("localDescription"))
  260. }
  261. // SetRemoteDescription sets the SessionDescription of the remote peer
  262. func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) (err error) {
  263. defer func() {
  264. if e := recover(); e != nil {
  265. err = recoveryToError(e)
  266. }
  267. }()
  268. promise := pc.underlying.Call("setRemoteDescription", sessionDescriptionToValue(&desc))
  269. _, err = awaitPromise(promise)
  270. return err
  271. }
  272. // RemoteDescription returns PendingRemoteDescription if it is not null and
  273. // otherwise it returns CurrentRemoteDescription. This property is used to
  274. // determine if setRemoteDescription has already been called.
  275. // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription
  276. func (pc *PeerConnection) RemoteDescription() *SessionDescription {
  277. return valueToSessionDescription(pc.underlying.Get("remoteDescription"))
  278. }
  279. // AddICECandidate accepts an ICE candidate string and adds it
  280. // to the existing set of candidates
  281. func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) (err error) {
  282. defer func() {
  283. if e := recover(); e != nil {
  284. err = recoveryToError(e)
  285. }
  286. }()
  287. promise := pc.underlying.Call("addIceCandidate", iceCandidateInitToValue(candidate))
  288. _, err = awaitPromise(promise)
  289. return err
  290. }
  291. // ICEConnectionState returns the ICE connection state of the
  292. // PeerConnection instance.
  293. func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
  294. return NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
  295. }
  296. // OnICECandidate sets an event handler which is invoked when a new ICE
  297. // candidate is found.
  298. func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) {
  299. if pc.onICECandidateHandler != nil {
  300. oldHandler := pc.onICECandidateHandler
  301. defer oldHandler.Release()
  302. }
  303. onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  304. candidate := valueToICECandidate(args[0].Get("candidate"))
  305. if candidate == nil && pc.onGatherCompleteHandler != nil {
  306. go pc.onGatherCompleteHandler()
  307. }
  308. go f(candidate)
  309. return js.Undefined()
  310. })
  311. pc.onICECandidateHandler = &onICECandidateHandler
  312. pc.underlying.Set("onicecandidate", onICECandidateHandler)
  313. }
  314. // OnICEGatheringStateChange sets an event handler which is invoked when the
  315. // ICE candidate gathering state has changed.
  316. func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
  317. if pc.onICEGatheringStateChangeHandler != nil {
  318. oldHandler := pc.onICEGatheringStateChangeHandler
  319. defer oldHandler.Release()
  320. }
  321. onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
  322. go f()
  323. return js.Undefined()
  324. })
  325. pc.onICEGatheringStateChangeHandler = &onICEGatheringStateChangeHandler
  326. pc.underlying.Set("onicegatheringstatechange", onICEGatheringStateChangeHandler)
  327. }
  328. // CreateDataChannel creates a new DataChannel object with the given label
  329. // and optional DataChannelInit used to configure properties of the
  330. // underlying channel such as data reliability.
  331. func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (_ *DataChannel, err error) {
  332. defer func() {
  333. if e := recover(); e != nil {
  334. err = recoveryToError(e)
  335. }
  336. }()
  337. channel := pc.underlying.Call("createDataChannel", label, dataChannelInitToValue(options))
  338. return &DataChannel{
  339. underlying: channel,
  340. api: pc.api,
  341. }, nil
  342. }
  343. // SetIdentityProvider is used to configure an identity provider to generate identity assertions
  344. func (pc *PeerConnection) SetIdentityProvider(provider string) (err error) {
  345. defer func() {
  346. if e := recover(); e != nil {
  347. err = recoveryToError(e)
  348. }
  349. }()
  350. pc.underlying.Call("setIdentityProvider", provider)
  351. return nil
  352. }
  353. // Close ends the PeerConnection
  354. func (pc *PeerConnection) Close() (err error) {
  355. defer func() {
  356. if e := recover(); e != nil {
  357. err = recoveryToError(e)
  358. }
  359. }()
  360. pc.underlying.Call("close")
  361. // Release any handlers as required by the syscall/js API.
  362. if pc.onSignalingStateChangeHandler != nil {
  363. pc.onSignalingStateChangeHandler.Release()
  364. }
  365. if pc.onDataChannelHandler != nil {
  366. pc.onDataChannelHandler.Release()
  367. }
  368. if pc.onNegotiationNeededHandler != nil {
  369. pc.onNegotiationNeededHandler.Release()
  370. }
  371. if pc.onConnectionStateChangeHandler != nil {
  372. pc.onConnectionStateChangeHandler.Release()
  373. }
  374. if pc.onICEConnectionStateChangeHandler != nil {
  375. pc.onICEConnectionStateChangeHandler.Release()
  376. }
  377. if pc.onICECandidateHandler != nil {
  378. pc.onICECandidateHandler.Release()
  379. }
  380. if pc.onICEGatheringStateChangeHandler != nil {
  381. pc.onICEGatheringStateChangeHandler.Release()
  382. }
  383. return nil
  384. }
  385. // CurrentLocalDescription represents the local description that was
  386. // successfully negotiated the last time the PeerConnection transitioned
  387. // into the stable state plus any local candidates that have been generated
  388. // by the ICEAgent since the offer or answer was created.
  389. func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
  390. desc := pc.underlying.Get("currentLocalDescription")
  391. return valueToSessionDescription(desc)
  392. }
  393. // PendingLocalDescription represents a local description that is in the
  394. // process of being negotiated plus any local candidates that have been
  395. // generated by the ICEAgent since the offer or answer was created. If the
  396. // PeerConnection is in the stable state, the value is null.
  397. func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
  398. desc := pc.underlying.Get("pendingLocalDescription")
  399. return valueToSessionDescription(desc)
  400. }
  401. // CurrentRemoteDescription represents the last remote description that was
  402. // successfully negotiated the last time the PeerConnection transitioned
  403. // into the stable state plus any remote candidates that have been supplied
  404. // via AddICECandidate() since the offer or answer was created.
  405. func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescription {
  406. desc := pc.underlying.Get("currentRemoteDescription")
  407. return valueToSessionDescription(desc)
  408. }
  409. // PendingRemoteDescription represents a remote description that is in the
  410. // process of being negotiated, complete with any remote candidates that
  411. // have been supplied via AddICECandidate() since the offer or answer was
  412. // created. If the PeerConnection is in the stable state, the value is
  413. // null.
  414. func (pc *PeerConnection) PendingRemoteDescription() *SessionDescription {
  415. desc := pc.underlying.Get("pendingRemoteDescription")
  416. return valueToSessionDescription(desc)
  417. }
  418. // SignalingState returns the signaling state of the PeerConnection instance.
  419. func (pc *PeerConnection) SignalingState() SignalingState {
  420. rawState := pc.underlying.Get("signalingState").String()
  421. return newSignalingState(rawState)
  422. }
  423. // ICEGatheringState attribute the ICE gathering state of the PeerConnection
  424. // instance.
  425. func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
  426. rawState := pc.underlying.Get("iceGatheringState").String()
  427. return NewICEGatheringState(rawState)
  428. }
  429. // ConnectionState attribute the connection state of the PeerConnection
  430. // instance.
  431. func (pc *PeerConnection) ConnectionState() PeerConnectionState {
  432. rawState := pc.underlying.Get("connectionState").String()
  433. return newPeerConnectionState(rawState)
  434. }
  435. func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
  436. pc.onGatherCompleteHandler = handler
  437. // If no onIceCandidate handler has been set provide an empty one
  438. // otherwise our onGatherCompleteHandler will not be executed
  439. if pc.onICECandidateHandler == nil {
  440. pc.OnICECandidate(func(i *ICECandidate) {})
  441. }
  442. }
  443. // AddTransceiverFromKind Create a new RtpTransceiver and adds it to the set of transceivers.
  444. func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RTPTransceiverInit) (transceiver *RTPTransceiver, err error) {
  445. defer func() {
  446. if e := recover(); e != nil {
  447. err = recoveryToError(e)
  448. }
  449. }()
  450. if len(init) == 1 {
  451. return &RTPTransceiver{
  452. underlying: pc.underlying.Call("addTransceiver", kind.String(), rtpTransceiverInitInitToValue(init[0])),
  453. }, err
  454. }
  455. return &RTPTransceiver{
  456. underlying: pc.underlying.Call("addTransceiver", kind.String()),
  457. }, err
  458. }
  459. // GetTransceivers returns the RtpTransceiver that are currently attached to this PeerConnection
  460. func (pc *PeerConnection) GetTransceivers() (transceivers []*RTPTransceiver) {
  461. rawTransceivers := pc.underlying.Call("getTransceivers")
  462. transceivers = make([]*RTPTransceiver, rawTransceivers.Length())
  463. for i := 0; i < rawTransceivers.Length(); i++ {
  464. transceivers[i] = &RTPTransceiver{
  465. underlying: rawTransceivers.Index(i),
  466. }
  467. }
  468. return
  469. }
  470. // SCTP returns the SCTPTransport for this PeerConnection
  471. //
  472. // The SCTP transport over which SCTP data is sent and received. If SCTP has not been negotiated, the value is nil.
  473. // https://www.w3.org/TR/webrtc/#attributes-15
  474. func (pc *PeerConnection) SCTP() *SCTPTransport {
  475. underlying := pc.underlying.Get("sctp")
  476. if underlying.IsNull() || underlying.IsUndefined() {
  477. return nil
  478. }
  479. return &SCTPTransport{
  480. underlying: underlying,
  481. }
  482. }
  483. // Converts a Configuration to js.Value so it can be passed
  484. // through to the JavaScript WebRTC API. Any zero values are converted to
  485. // js.Undefined(), which will result in the default value being used.
  486. func configurationToValue(configuration Configuration) js.Value {
  487. return js.ValueOf(map[string]interface{}{
  488. "iceServers": iceServersToValue(configuration.ICEServers),
  489. "iceTransportPolicy": stringEnumToValueOrUndefined(configuration.ICETransportPolicy.String()),
  490. "bundlePolicy": stringEnumToValueOrUndefined(configuration.BundlePolicy.String()),
  491. "rtcpMuxPolicy": stringEnumToValueOrUndefined(configuration.RTCPMuxPolicy.String()),
  492. "peerIdentity": stringToValueOrUndefined(configuration.PeerIdentity),
  493. "iceCandidatePoolSize": uint8ToValueOrUndefined(configuration.ICECandidatePoolSize),
  494. // Note: Certificates are not currently supported.
  495. // "certificates": configuration.Certificates,
  496. })
  497. }
  498. func iceServersToValue(iceServers []ICEServer) js.Value {
  499. if len(iceServers) == 0 {
  500. return js.Undefined()
  501. }
  502. maps := make([]interface{}, len(iceServers))
  503. for i, server := range iceServers {
  504. maps[i] = iceServerToValue(server)
  505. }
  506. return js.ValueOf(maps)
  507. }
  508. func oauthCredentialToValue(o OAuthCredential) js.Value {
  509. out := map[string]interface{}{
  510. "MACKey": o.MACKey,
  511. "AccessToken": o.AccessToken,
  512. }
  513. return js.ValueOf(out)
  514. }
  515. func iceServerToValue(server ICEServer) js.Value {
  516. out := map[string]interface{}{
  517. "urls": stringsToValue(server.URLs), // required
  518. }
  519. if server.Username != "" {
  520. out["username"] = stringToValueOrUndefined(server.Username)
  521. }
  522. if server.Credential != nil {
  523. switch t := server.Credential.(type) {
  524. case string:
  525. out["credential"] = stringToValueOrUndefined(t)
  526. case OAuthCredential:
  527. out["credential"] = oauthCredentialToValue(t)
  528. }
  529. }
  530. out["credentialType"] = stringEnumToValueOrUndefined(server.CredentialType.String())
  531. return js.ValueOf(out)
  532. }
  533. func valueToConfiguration(configValue js.Value) Configuration {
  534. if configValue.IsNull() || configValue.IsUndefined() {
  535. return Configuration{}
  536. }
  537. return Configuration{
  538. ICEServers: valueToICEServers(configValue.Get("iceServers")),
  539. ICETransportPolicy: NewICETransportPolicy(valueToStringOrZero(configValue.Get("iceTransportPolicy"))),
  540. BundlePolicy: newBundlePolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
  541. RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("rtcpMuxPolicy"))),
  542. PeerIdentity: valueToStringOrZero(configValue.Get("peerIdentity")),
  543. ICECandidatePoolSize: valueToUint8OrZero(configValue.Get("iceCandidatePoolSize")),
  544. // Note: Certificates are not supported.
  545. // Certificates []Certificate
  546. }
  547. }
  548. func valueToICEServers(iceServersValue js.Value) []ICEServer {
  549. if iceServersValue.IsNull() || iceServersValue.IsUndefined() {
  550. return nil
  551. }
  552. iceServers := make([]ICEServer, iceServersValue.Length())
  553. for i := 0; i < iceServersValue.Length(); i++ {
  554. iceServers[i] = valueToICEServer(iceServersValue.Index(i))
  555. }
  556. return iceServers
  557. }
  558. func valueToICECredential(iceCredentialValue js.Value) interface{} {
  559. if iceCredentialValue.IsNull() || iceCredentialValue.IsUndefined() {
  560. return nil
  561. }
  562. if iceCredentialValue.Type() == js.TypeString {
  563. return iceCredentialValue.String()
  564. }
  565. if iceCredentialValue.Type() == js.TypeObject {
  566. return OAuthCredential{
  567. MACKey: iceCredentialValue.Get("MACKey").String(),
  568. AccessToken: iceCredentialValue.Get("AccessToken").String(),
  569. }
  570. }
  571. return nil
  572. }
  573. func valueToICEServer(iceServerValue js.Value) ICEServer {
  574. tpe, err := newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType")))
  575. if err != nil {
  576. tpe = ICECredentialTypePassword
  577. }
  578. s := ICEServer{
  579. URLs: valueToStrings(iceServerValue.Get("urls")), // required
  580. Username: valueToStringOrZero(iceServerValue.Get("username")),
  581. // Note: Credential and CredentialType are not currently supported.
  582. Credential: valueToICECredential(iceServerValue.Get("credential")),
  583. CredentialType: tpe,
  584. }
  585. return s
  586. }
  587. func valueToICECandidate(val js.Value) *ICECandidate {
  588. if val.IsNull() || val.IsUndefined() {
  589. return nil
  590. }
  591. if val.Get("protocol").IsUndefined() && !val.Get("candidate").IsUndefined() {
  592. // Missing some fields, assume it's Firefox and parse SDP candidate.
  593. c, err := ice.UnmarshalCandidate(val.Get("candidate").String())
  594. if err != nil {
  595. return nil
  596. }
  597. iceCandidate, err := newICECandidateFromICE(c)
  598. if err != nil {
  599. return nil
  600. }
  601. return &iceCandidate
  602. }
  603. protocol, _ := NewICEProtocol(val.Get("protocol").String())
  604. candidateType, _ := NewICECandidateType(val.Get("type").String())
  605. return &ICECandidate{
  606. Foundation: val.Get("foundation").String(),
  607. Priority: valueToUint32OrZero(val.Get("priority")),
  608. Address: val.Get("address").String(),
  609. Protocol: protocol,
  610. Port: valueToUint16OrZero(val.Get("port")),
  611. Typ: candidateType,
  612. Component: stringToComponentIDOrZero(val.Get("component").String()),
  613. RelatedAddress: val.Get("relatedAddress").String(),
  614. RelatedPort: valueToUint16OrZero(val.Get("relatedPort")),
  615. }
  616. }
  617. func stringToComponentIDOrZero(val string) uint16 {
  618. // See: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceComponent
  619. switch val {
  620. case "rtp":
  621. return 1
  622. case "rtcp":
  623. return 2
  624. }
  625. return 0
  626. }
  627. func sessionDescriptionToValue(desc *SessionDescription) js.Value {
  628. if desc == nil {
  629. return js.Undefined()
  630. }
  631. return js.ValueOf(map[string]interface{}{
  632. "type": desc.Type.String(),
  633. "sdp": desc.SDP,
  634. })
  635. }
  636. func valueToSessionDescription(descValue js.Value) *SessionDescription {
  637. if descValue.IsNull() || descValue.IsUndefined() {
  638. return nil
  639. }
  640. return &SessionDescription{
  641. Type: NewSDPType(descValue.Get("type").String()),
  642. SDP: descValue.Get("sdp").String(),
  643. }
  644. }
  645. func offerOptionsToValue(offerOptions *OfferOptions) js.Value {
  646. if offerOptions == nil {
  647. return js.Undefined()
  648. }
  649. return js.ValueOf(map[string]interface{}{
  650. "iceRestart": offerOptions.ICERestart,
  651. "voiceActivityDetection": offerOptions.VoiceActivityDetection,
  652. })
  653. }
  654. func answerOptionsToValue(answerOptions *AnswerOptions) js.Value {
  655. if answerOptions == nil {
  656. return js.Undefined()
  657. }
  658. return js.ValueOf(map[string]interface{}{
  659. "voiceActivityDetection": answerOptions.VoiceActivityDetection,
  660. })
  661. }
  662. func iceCandidateInitToValue(candidate ICECandidateInit) js.Value {
  663. return js.ValueOf(map[string]interface{}{
  664. "candidate": candidate.Candidate,
  665. "sdpMid": stringPointerToValue(candidate.SDPMid),
  666. "sdpMLineIndex": uint16PointerToValue(candidate.SDPMLineIndex),
  667. "usernameFragment": stringPointerToValue(candidate.UsernameFragment),
  668. })
  669. }
  670. func dataChannelInitToValue(options *DataChannelInit) js.Value {
  671. if options == nil {
  672. return js.Undefined()
  673. }
  674. maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime)
  675. return js.ValueOf(map[string]interface{}{
  676. "ordered": boolPointerToValue(options.Ordered),
  677. "maxPacketLifeTime": maxPacketLifeTime,
  678. // See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
  679. // Chrome calls this "maxRetransmitTime"
  680. "maxRetransmitTime": maxPacketLifeTime,
  681. "maxRetransmits": uint16PointerToValue(options.MaxRetransmits),
  682. "protocol": stringPointerToValue(options.Protocol),
  683. "negotiated": boolPointerToValue(options.Negotiated),
  684. "id": uint16PointerToValue(options.ID),
  685. })
  686. }
  687. func rtpTransceiverInitInitToValue(init RTPTransceiverInit) js.Value {
  688. return js.ValueOf(map[string]interface{}{
  689. "direction": init.Direction.String(),
  690. })
  691. }