peerconnection_js.go 27 KB

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