| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- //go:build js && wasm
- // +build js,wasm
- // Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document.
- package webrtc
- import (
- "syscall/js"
- "github.com/pion/ice/v2"
- "github.com/pion/webrtc/v3/pkg/rtcerr"
- )
- // PeerConnection represents a WebRTC connection that establishes a
- // peer-to-peer communications with another PeerConnection instance in a
- // browser, or to another endpoint implementing the required protocols.
- type PeerConnection struct {
- // Pointer to the underlying JavaScript RTCPeerConnection object.
- underlying js.Value
- // Keep track of handlers/callbacks so we can call Release as required by the
- // syscall/js API. Initially nil.
- onSignalingStateChangeHandler *js.Func
- onDataChannelHandler *js.Func
- onNegotiationNeededHandler *js.Func
- onConnectionStateChangeHandler *js.Func
- onICEConnectionStateChangeHandler *js.Func
- onICECandidateHandler *js.Func
- onICEGatheringStateChangeHandler *js.Func
- // Used by GatheringCompletePromise
- onGatherCompleteHandler func()
- // A reference to the associated API state used by this connection
- api *API
- }
- // NewPeerConnection creates a peerconnection.
- func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
- api := NewAPI()
- return api.NewPeerConnection(configuration)
- }
- // NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
- func (api *API) NewPeerConnection(configuration Configuration) (_ *PeerConnection, err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- configMap := configurationToValue(configuration)
- underlying := js.Global().Get("window").Get("RTCPeerConnection").New(configMap)
- return &PeerConnection{
- underlying: underlying,
- api: api,
- }, nil
- }
- // JSValue returns the underlying PeerConnection
- func (pc *PeerConnection) JSValue() js.Value {
- return pc.underlying
- }
- // OnSignalingStateChange sets an event handler which is invoked when the
- // peer connection's signaling state changes
- func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) {
- if pc.onSignalingStateChangeHandler != nil {
- oldHandler := pc.onSignalingStateChangeHandler
- defer oldHandler.Release()
- }
- onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- state := newSignalingState(args[0].String())
- go f(state)
- return js.Undefined()
- })
- pc.onSignalingStateChangeHandler = &onSignalingStateChangeHandler
- pc.underlying.Set("onsignalingstatechange", onSignalingStateChangeHandler)
- }
- // OnDataChannel sets an event handler which is invoked when a data
- // channel message arrives from a remote peer.
- func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
- if pc.onDataChannelHandler != nil {
- oldHandler := pc.onDataChannelHandler
- defer oldHandler.Release()
- }
- onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- // pion/webrtc/projects/15
- // This reference to the underlying DataChannel doesn't know
- // about any other references to the same DataChannel. This might result in
- // memory leaks where we don't clean up handler functions. Could possibly fix
- // by keeping a mutex-protected list of all DataChannel references as a
- // property of this PeerConnection, but at the cost of additional overhead.
- dataChannel := &DataChannel{
- underlying: args[0].Get("channel"),
- api: pc.api,
- }
- go f(dataChannel)
- return js.Undefined()
- })
- pc.onDataChannelHandler = &onDataChannelHandler
- pc.underlying.Set("ondatachannel", onDataChannelHandler)
- }
- // OnNegotiationNeeded sets an event handler which is invoked when
- // a change has occurred which requires session negotiation
- func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
- if pc.onNegotiationNeededHandler != nil {
- oldHandler := pc.onNegotiationNeededHandler
- defer oldHandler.Release()
- }
- onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- go f()
- return js.Undefined()
- })
- pc.onNegotiationNeededHandler = &onNegotiationNeededHandler
- pc.underlying.Set("onnegotiationneeded", onNegotiationNeededHandler)
- }
- // OnICEConnectionStateChange sets an event handler which is called
- // when an ICE connection state is changed.
- func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
- if pc.onICEConnectionStateChangeHandler != nil {
- oldHandler := pc.onICEConnectionStateChangeHandler
- defer oldHandler.Release()
- }
- onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- connectionState := NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
- go f(connectionState)
- return js.Undefined()
- })
- pc.onICEConnectionStateChangeHandler = &onICEConnectionStateChangeHandler
- pc.underlying.Set("oniceconnectionstatechange", onICEConnectionStateChangeHandler)
- }
- // OnConnectionStateChange sets an event handler which is called
- // when an PeerConnectionState is changed.
- func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
- if pc.onConnectionStateChangeHandler != nil {
- oldHandler := pc.onConnectionStateChangeHandler
- defer oldHandler.Release()
- }
- onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- connectionState := newPeerConnectionState(pc.underlying.Get("connectionState").String())
- go f(connectionState)
- return js.Undefined()
- })
- pc.onConnectionStateChangeHandler = &onConnectionStateChangeHandler
- pc.underlying.Set("onconnectionstatechange", onConnectionStateChangeHandler)
- }
- func (pc *PeerConnection) checkConfiguration(configuration Configuration) error {
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
- if pc.ConnectionState() == PeerConnectionStateClosed {
- return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
- }
- existingConfig := pc.GetConfiguration()
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
- if configuration.PeerIdentity != "" {
- if configuration.PeerIdentity != existingConfig.PeerIdentity {
- return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
- }
- }
- // https://github.com/pion/webrtc/issues/513
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
- // if len(configuration.Certificates) > 0 {
- // if len(configuration.Certificates) != len(existingConfiguration.Certificates) {
- // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
- // }
- // for i, certificate := range configuration.Certificates {
- // if !pc.configuration.Certificates[i].Equals(certificate) {
- // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
- // }
- // }
- // pc.configuration.Certificates = configuration.Certificates
- // }
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
- if configuration.BundlePolicy != BundlePolicy(Unknown) {
- if configuration.BundlePolicy != existingConfig.BundlePolicy {
- return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
- }
- }
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
- if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
- if configuration.RTCPMuxPolicy != existingConfig.RTCPMuxPolicy {
- return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
- }
- }
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
- if configuration.ICECandidatePoolSize != 0 {
- if configuration.ICECandidatePoolSize != existingConfig.ICECandidatePoolSize &&
- pc.LocalDescription() != nil {
- return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
- }
- }
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
- if len(configuration.ICEServers) > 0 {
- // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
- for _, server := range configuration.ICEServers {
- if _, err := server.validate(); err != nil {
- return err
- }
- }
- }
- return nil
- }
- // SetConfiguration updates the configuration of this PeerConnection object.
- func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- if err := pc.checkConfiguration(configuration); err != nil {
- return err
- }
- configMap := configurationToValue(configuration)
- pc.underlying.Call("setConfiguration", configMap)
- return nil
- }
- // GetConfiguration returns a Configuration object representing the current
- // configuration of this PeerConnection object. The returned object is a
- // copy and direct mutation on it will not take affect until SetConfiguration
- // has been called with Configuration passed as its only argument.
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration
- func (pc *PeerConnection) GetConfiguration() Configuration {
- return valueToConfiguration(pc.underlying.Call("getConfiguration"))
- }
- // CreateOffer starts the PeerConnection and generates the localDescription
- func (pc *PeerConnection) CreateOffer(options *OfferOptions) (_ SessionDescription, err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- promise := pc.underlying.Call("createOffer", offerOptionsToValue(options))
- desc, err := awaitPromise(promise)
- if err != nil {
- return SessionDescription{}, err
- }
- return *valueToSessionDescription(desc), nil
- }
- // CreateAnswer starts the PeerConnection and generates the localDescription
- func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (_ SessionDescription, err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- promise := pc.underlying.Call("createAnswer", answerOptionsToValue(options))
- desc, err := awaitPromise(promise)
- if err != nil {
- return SessionDescription{}, err
- }
- return *valueToSessionDescription(desc), nil
- }
- // SetLocalDescription sets the SessionDescription of the local peer
- func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- promise := pc.underlying.Call("setLocalDescription", sessionDescriptionToValue(&desc))
- _, err = awaitPromise(promise)
- return err
- }
- // LocalDescription returns PendingLocalDescription if it is not null and
- // otherwise it returns CurrentLocalDescription. This property is used to
- // determine if setLocalDescription has already been called.
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription
- func (pc *PeerConnection) LocalDescription() *SessionDescription {
- return valueToSessionDescription(pc.underlying.Get("localDescription"))
- }
- // SetRemoteDescription sets the SessionDescription of the remote peer
- func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- promise := pc.underlying.Call("setRemoteDescription", sessionDescriptionToValue(&desc))
- _, err = awaitPromise(promise)
- return err
- }
- // RemoteDescription returns PendingRemoteDescription if it is not null and
- // otherwise it returns CurrentRemoteDescription. This property is used to
- // determine if setRemoteDescription has already been called.
- // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription
- func (pc *PeerConnection) RemoteDescription() *SessionDescription {
- return valueToSessionDescription(pc.underlying.Get("remoteDescription"))
- }
- // AddICECandidate accepts an ICE candidate string and adds it
- // to the existing set of candidates
- func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- promise := pc.underlying.Call("addIceCandidate", iceCandidateInitToValue(candidate))
- _, err = awaitPromise(promise)
- return err
- }
- // ICEConnectionState returns the ICE connection state of the
- // PeerConnection instance.
- func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
- return NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
- }
- // OnICECandidate sets an event handler which is invoked when a new ICE
- // candidate is found.
- func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) {
- if pc.onICECandidateHandler != nil {
- oldHandler := pc.onICECandidateHandler
- defer oldHandler.Release()
- }
- onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- candidate := valueToICECandidate(args[0].Get("candidate"))
- if candidate == nil && pc.onGatherCompleteHandler != nil {
- go pc.onGatherCompleteHandler()
- }
- go f(candidate)
- return js.Undefined()
- })
- pc.onICECandidateHandler = &onICECandidateHandler
- pc.underlying.Set("onicecandidate", onICECandidateHandler)
- }
- // OnICEGatheringStateChange sets an event handler which is invoked when the
- // ICE candidate gathering state has changed.
- func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
- if pc.onICEGatheringStateChangeHandler != nil {
- oldHandler := pc.onICEGatheringStateChangeHandler
- defer oldHandler.Release()
- }
- onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
- go f()
- return js.Undefined()
- })
- pc.onICEGatheringStateChangeHandler = &onICEGatheringStateChangeHandler
- pc.underlying.Set("onicegatheringstatechange", onICEGatheringStateChangeHandler)
- }
- // CreateDataChannel creates a new DataChannel object with the given label
- // and optional DataChannelInit used to configure properties of the
- // underlying channel such as data reliability.
- func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (_ *DataChannel, err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- channel := pc.underlying.Call("createDataChannel", label, dataChannelInitToValue(options))
- return &DataChannel{
- underlying: channel,
- api: pc.api,
- }, nil
- }
- // SetIdentityProvider is used to configure an identity provider to generate identity assertions
- func (pc *PeerConnection) SetIdentityProvider(provider string) (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- pc.underlying.Call("setIdentityProvider", provider)
- return nil
- }
- // Close ends the PeerConnection
- func (pc *PeerConnection) Close() (err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- pc.underlying.Call("close")
- // Release any handlers as required by the syscall/js API.
- if pc.onSignalingStateChangeHandler != nil {
- pc.onSignalingStateChangeHandler.Release()
- }
- if pc.onDataChannelHandler != nil {
- pc.onDataChannelHandler.Release()
- }
- if pc.onNegotiationNeededHandler != nil {
- pc.onNegotiationNeededHandler.Release()
- }
- if pc.onConnectionStateChangeHandler != nil {
- pc.onConnectionStateChangeHandler.Release()
- }
- if pc.onICEConnectionStateChangeHandler != nil {
- pc.onICEConnectionStateChangeHandler.Release()
- }
- if pc.onICECandidateHandler != nil {
- pc.onICECandidateHandler.Release()
- }
- if pc.onICEGatheringStateChangeHandler != nil {
- pc.onICEGatheringStateChangeHandler.Release()
- }
- return nil
- }
- // CurrentLocalDescription represents the local description that was
- // successfully negotiated the last time the PeerConnection transitioned
- // into the stable state plus any local candidates that have been generated
- // by the ICEAgent since the offer or answer was created.
- func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
- desc := pc.underlying.Get("currentLocalDescription")
- return valueToSessionDescription(desc)
- }
- // PendingLocalDescription represents a local description that is in the
- // process of being negotiated plus any local candidates that have been
- // generated by the ICEAgent since the offer or answer was created. If the
- // PeerConnection is in the stable state, the value is null.
- func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
- desc := pc.underlying.Get("pendingLocalDescription")
- return valueToSessionDescription(desc)
- }
- // CurrentRemoteDescription represents the last remote description that was
- // successfully negotiated the last time the PeerConnection transitioned
- // into the stable state plus any remote candidates that have been supplied
- // via AddICECandidate() since the offer or answer was created.
- func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescription {
- desc := pc.underlying.Get("currentRemoteDescription")
- return valueToSessionDescription(desc)
- }
- // PendingRemoteDescription represents a remote description that is in the
- // process of being negotiated, complete with any remote candidates that
- // have been supplied via AddICECandidate() since the offer or answer was
- // created. If the PeerConnection is in the stable state, the value is
- // null.
- func (pc *PeerConnection) PendingRemoteDescription() *SessionDescription {
- desc := pc.underlying.Get("pendingRemoteDescription")
- return valueToSessionDescription(desc)
- }
- // SignalingState returns the signaling state of the PeerConnection instance.
- func (pc *PeerConnection) SignalingState() SignalingState {
- rawState := pc.underlying.Get("signalingState").String()
- return newSignalingState(rawState)
- }
- // ICEGatheringState attribute the ICE gathering state of the PeerConnection
- // instance.
- func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
- rawState := pc.underlying.Get("iceGatheringState").String()
- return NewICEGatheringState(rawState)
- }
- // ConnectionState attribute the connection state of the PeerConnection
- // instance.
- func (pc *PeerConnection) ConnectionState() PeerConnectionState {
- rawState := pc.underlying.Get("connectionState").String()
- return newPeerConnectionState(rawState)
- }
- func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
- pc.onGatherCompleteHandler = handler
- // If no onIceCandidate handler has been set provide an empty one
- // otherwise our onGatherCompleteHandler will not be executed
- if pc.onICECandidateHandler == nil {
- pc.OnICECandidate(func(i *ICECandidate) {})
- }
- }
- // AddTransceiverFromKind Create a new RtpTransceiver and adds it to the set of transceivers.
- func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RTPTransceiverInit) (transceiver *RTPTransceiver, err error) {
- defer func() {
- if e := recover(); e != nil {
- err = recoveryToError(e)
- }
- }()
- if len(init) == 1 {
- return &RTPTransceiver{
- underlying: pc.underlying.Call("addTransceiver", kind.String(), rtpTransceiverInitInitToValue(init[0])),
- }, err
- }
- return &RTPTransceiver{
- underlying: pc.underlying.Call("addTransceiver", kind.String()),
- }, err
- }
- // GetTransceivers returns the RtpTransceiver that are currently attached to this PeerConnection
- func (pc *PeerConnection) GetTransceivers() (transceivers []*RTPTransceiver) {
- rawTransceivers := pc.underlying.Call("getTransceivers")
- transceivers = make([]*RTPTransceiver, rawTransceivers.Length())
- for i := 0; i < rawTransceivers.Length(); i++ {
- transceivers[i] = &RTPTransceiver{
- underlying: rawTransceivers.Index(i),
- }
- }
- return
- }
- // SCTP returns the SCTPTransport for this PeerConnection
- //
- // The SCTP transport over which SCTP data is sent and received. If SCTP has not been negotiated, the value is nil.
- // https://www.w3.org/TR/webrtc/#attributes-15
- func (pc *PeerConnection) SCTP() *SCTPTransport {
- underlying := pc.underlying.Get("sctp")
- if underlying.IsNull() || underlying.IsUndefined() {
- return nil
- }
- return &SCTPTransport{
- underlying: underlying,
- }
- }
- // Converts a Configuration to js.Value so it can be passed
- // through to the JavaScript WebRTC API. Any zero values are converted to
- // js.Undefined(), which will result in the default value being used.
- func configurationToValue(configuration Configuration) js.Value {
- return js.ValueOf(map[string]interface{}{
- "iceServers": iceServersToValue(configuration.ICEServers),
- "iceTransportPolicy": stringEnumToValueOrUndefined(configuration.ICETransportPolicy.String()),
- "bundlePolicy": stringEnumToValueOrUndefined(configuration.BundlePolicy.String()),
- "rtcpMuxPolicy": stringEnumToValueOrUndefined(configuration.RTCPMuxPolicy.String()),
- "peerIdentity": stringToValueOrUndefined(configuration.PeerIdentity),
- "iceCandidatePoolSize": uint8ToValueOrUndefined(configuration.ICECandidatePoolSize),
- // Note: Certificates are not currently supported.
- // "certificates": configuration.Certificates,
- })
- }
- func iceServersToValue(iceServers []ICEServer) js.Value {
- if len(iceServers) == 0 {
- return js.Undefined()
- }
- maps := make([]interface{}, len(iceServers))
- for i, server := range iceServers {
- maps[i] = iceServerToValue(server)
- }
- return js.ValueOf(maps)
- }
- func oauthCredentialToValue(o OAuthCredential) js.Value {
- out := map[string]interface{}{
- "MACKey": o.MACKey,
- "AccessToken": o.AccessToken,
- }
- return js.ValueOf(out)
- }
- func iceServerToValue(server ICEServer) js.Value {
- out := map[string]interface{}{
- "urls": stringsToValue(server.URLs), // required
- }
- if server.Username != "" {
- out["username"] = stringToValueOrUndefined(server.Username)
- }
- if server.Credential != nil {
- switch t := server.Credential.(type) {
- case string:
- out["credential"] = stringToValueOrUndefined(t)
- case OAuthCredential:
- out["credential"] = oauthCredentialToValue(t)
- }
- }
- out["credentialType"] = stringEnumToValueOrUndefined(server.CredentialType.String())
- return js.ValueOf(out)
- }
- func valueToConfiguration(configValue js.Value) Configuration {
- if configValue.IsNull() || configValue.IsUndefined() {
- return Configuration{}
- }
- return Configuration{
- ICEServers: valueToICEServers(configValue.Get("iceServers")),
- ICETransportPolicy: NewICETransportPolicy(valueToStringOrZero(configValue.Get("iceTransportPolicy"))),
- BundlePolicy: newBundlePolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
- RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("rtcpMuxPolicy"))),
- PeerIdentity: valueToStringOrZero(configValue.Get("peerIdentity")),
- ICECandidatePoolSize: valueToUint8OrZero(configValue.Get("iceCandidatePoolSize")),
- // Note: Certificates are not supported.
- // Certificates []Certificate
- }
- }
- func valueToICEServers(iceServersValue js.Value) []ICEServer {
- if iceServersValue.IsNull() || iceServersValue.IsUndefined() {
- return nil
- }
- iceServers := make([]ICEServer, iceServersValue.Length())
- for i := 0; i < iceServersValue.Length(); i++ {
- iceServers[i] = valueToICEServer(iceServersValue.Index(i))
- }
- return iceServers
- }
- func valueToICECredential(iceCredentialValue js.Value) interface{} {
- if iceCredentialValue.IsNull() || iceCredentialValue.IsUndefined() {
- return nil
- }
- if iceCredentialValue.Type() == js.TypeString {
- return iceCredentialValue.String()
- }
- if iceCredentialValue.Type() == js.TypeObject {
- return OAuthCredential{
- MACKey: iceCredentialValue.Get("MACKey").String(),
- AccessToken: iceCredentialValue.Get("AccessToken").String(),
- }
- }
- return nil
- }
- func valueToICEServer(iceServerValue js.Value) ICEServer {
- tpe, err := newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType")))
- if err != nil {
- tpe = ICECredentialTypePassword
- }
- s := ICEServer{
- URLs: valueToStrings(iceServerValue.Get("urls")), // required
- Username: valueToStringOrZero(iceServerValue.Get("username")),
- // Note: Credential and CredentialType are not currently supported.
- Credential: valueToICECredential(iceServerValue.Get("credential")),
- CredentialType: tpe,
- }
- return s
- }
- func valueToICECandidate(val js.Value) *ICECandidate {
- if val.IsNull() || val.IsUndefined() {
- return nil
- }
- if val.Get("protocol").IsUndefined() && !val.Get("candidate").IsUndefined() {
- // Missing some fields, assume it's Firefox and parse SDP candidate.
- c, err := ice.UnmarshalCandidate(val.Get("candidate").String())
- if err != nil {
- return nil
- }
- iceCandidate, err := newICECandidateFromICE(c)
- if err != nil {
- return nil
- }
- return &iceCandidate
- }
- protocol, _ := NewICEProtocol(val.Get("protocol").String())
- candidateType, _ := NewICECandidateType(val.Get("type").String())
- return &ICECandidate{
- Foundation: val.Get("foundation").String(),
- Priority: valueToUint32OrZero(val.Get("priority")),
- Address: val.Get("address").String(),
- Protocol: protocol,
- Port: valueToUint16OrZero(val.Get("port")),
- Typ: candidateType,
- Component: stringToComponentIDOrZero(val.Get("component").String()),
- RelatedAddress: val.Get("relatedAddress").String(),
- RelatedPort: valueToUint16OrZero(val.Get("relatedPort")),
- }
- }
- func stringToComponentIDOrZero(val string) uint16 {
- // See: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceComponent
- switch val {
- case "rtp":
- return 1
- case "rtcp":
- return 2
- }
- return 0
- }
- func sessionDescriptionToValue(desc *SessionDescription) js.Value {
- if desc == nil {
- return js.Undefined()
- }
- return js.ValueOf(map[string]interface{}{
- "type": desc.Type.String(),
- "sdp": desc.SDP,
- })
- }
- func valueToSessionDescription(descValue js.Value) *SessionDescription {
- if descValue.IsNull() || descValue.IsUndefined() {
- return nil
- }
- return &SessionDescription{
- Type: NewSDPType(descValue.Get("type").String()),
- SDP: descValue.Get("sdp").String(),
- }
- }
- func offerOptionsToValue(offerOptions *OfferOptions) js.Value {
- if offerOptions == nil {
- return js.Undefined()
- }
- return js.ValueOf(map[string]interface{}{
- "iceRestart": offerOptions.ICERestart,
- "voiceActivityDetection": offerOptions.VoiceActivityDetection,
- })
- }
- func answerOptionsToValue(answerOptions *AnswerOptions) js.Value {
- if answerOptions == nil {
- return js.Undefined()
- }
- return js.ValueOf(map[string]interface{}{
- "voiceActivityDetection": answerOptions.VoiceActivityDetection,
- })
- }
- func iceCandidateInitToValue(candidate ICECandidateInit) js.Value {
- return js.ValueOf(map[string]interface{}{
- "candidate": candidate.Candidate,
- "sdpMid": stringPointerToValue(candidate.SDPMid),
- "sdpMLineIndex": uint16PointerToValue(candidate.SDPMLineIndex),
- "usernameFragment": stringPointerToValue(candidate.UsernameFragment),
- })
- }
- func dataChannelInitToValue(options *DataChannelInit) js.Value {
- if options == nil {
- return js.Undefined()
- }
- maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime)
- return js.ValueOf(map[string]interface{}{
- "ordered": boolPointerToValue(options.Ordered),
- "maxPacketLifeTime": maxPacketLifeTime,
- // See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
- // Chrome calls this "maxRetransmitTime"
- "maxRetransmitTime": maxPacketLifeTime,
- "maxRetransmits": uint16PointerToValue(options.MaxRetransmits),
- "protocol": stringPointerToValue(options.Protocol),
- "negotiated": boolPointerToValue(options.Negotiated),
- "id": uint16PointerToValue(options.ID),
- })
- }
- func rtpTransceiverInitInitToValue(init RTPTransceiverInit) js.Value {
- return js.ValueOf(map[string]interface{}{
- "direction": init.Direction.String(),
- })
- }
|