Skip to content

Commit

Permalink
WASM: Fallback to SDP line for candiate callback
Browse files Browse the repository at this point in the history
On Firefox, the RTCIceCandidate interface appears to just be an
RTCIceCandidateInit in disguise. That is, it does not have properties
for each individual component of the candidate line. It only has
the raw SDP string in the candidate property.

This change falls back to parsing the candidate line if some expected
property is missing when preparing the candidate for the callback
OnICECandidate.

https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate

https://caniuse.com/#feat=mdn-api_rtcicecandidate_priority
  • Loading branch information
saljam authored and Sean-Der committed May 26, 2020
1 parent 7b37f0f commit 7a95bb8
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 24 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -158,6 +158,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
* [Cedric Fung](https://github.com/cedricfung)
* [Norman Rasmussen](https://github.com/normanr) - *Fix Empty DataChannel messages*
* [Josh Bleecher Snyder](https://github.com/josharian)
* [salmān aljammāz](https://github.com/saljam)

### License
MIT License - see [LICENSE](LICENSE) for full text
24 changes: 0 additions & 24 deletions ice_go.go
Expand Up @@ -2,33 +2,9 @@

package webrtc

import "github.com/pion/sdp/v2"

// NewICETransport creates a new NewICETransport.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func (api *API) NewICETransport(gatherer *ICEGatherer) *ICETransport {
return NewICETransport(gatherer, api.settingEngine.LoggerFactory)
}

func newICECandidateFromSDP(c sdp.ICECandidate) (ICECandidate, error) {
typ, err := NewICECandidateType(c.Typ)
if err != nil {
return ICECandidate{}, err
}
protocol, err := NewICEProtocol(c.Protocol)
if err != nil {
return ICECandidate{}, err
}
return ICECandidate{
Foundation: c.Foundation,
Priority: c.Priority,
Address: c.Address,
Protocol: protocol,
Port: c.Port,
Component: c.Component,
Typ: typ,
RelatedAddress: c.RelatedAddress,
RelatedPort: c.RelatedPort,
}, nil
}
22 changes: 22 additions & 0 deletions icecandidate.go
Expand Up @@ -153,6 +153,28 @@ func iceCandidateToSDP(c ICECandidate) sdp.ICECandidate {
}
}

func newICECandidateFromSDP(c sdp.ICECandidate) (ICECandidate, error) {
typ, err := NewICECandidateType(c.Typ)
if err != nil {
return ICECandidate{}, err
}
protocol, err := NewICEProtocol(c.Protocol)
if err != nil {
return ICECandidate{}, err
}
return ICECandidate{
Foundation: c.Foundation,
Priority: c.Priority,
Address: c.Address,
Protocol: protocol,
Port: c.Port,
Component: c.Component,
Typ: typ,
RelatedAddress: c.RelatedAddress,
RelatedPort: c.RelatedPort,
}, nil
}

// ToJSON returns an ICECandidateInit
// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
func (c ICECandidate) ToJSON() ICECandidateInit {
Expand Down
14 changes: 14 additions & 0 deletions peerconnection_js.go
Expand Up @@ -6,6 +6,7 @@ package webrtc
import (
"syscall/js"

"github.com/pion/sdp/v2"
"github.com/pion/webrtc/v2/pkg/rtcerr"
)

Expand Down Expand Up @@ -527,6 +528,19 @@ func valueToICECandidate(val js.Value) *ICECandidate {
if jsValueIsNull(val) || jsValueIsUndefined(val) {
return nil
}
if jsValueIsUndefined(val.Get("protocol")) && !jsValueIsUndefined(val.Get("candidate")) {
// Missing some fields, assume it's Firefox and parse SDP candidate.
attribute := sdp.NewAttribute("candidate", val.Get("candidate").String())
sdpCandidate, err := attribute.ToICECandidate()
if err != nil {
return nil
}
iceCandidate, err := newICECandidateFromSDP(sdpCandidate)
if err != nil {
return nil
}
return &iceCandidate
}
protocol, _ := NewICEProtocol(val.Get("protocol").String())
candidateType, _ := NewICECandidateType(val.Get("type").String())
return &ICECandidate{
Expand Down
69 changes: 69 additions & 0 deletions peerconnection_js_test.go
@@ -0,0 +1,69 @@
package webrtc

import (
"encoding/json"
"syscall/js"
"testing"

"github.com/stretchr/testify/assert"
)

func TestValueToICECandidate(t *testing.T) {
testCases := []struct {
jsonCandidate string
expect ICECandidate
}{
{
// Firefox-style ICECandidateInit:
`{"candidate":"1966762133 1 udp 2122260222 192.168.20.128 47298 typ srflx raddr 203.0.113.1 rport 5000"}`,
ICECandidate{
Foundation: "1966762133",
Priority: 2122260222,
Address: "192.168.20.128",
Protocol: ICEProtocolUDP,
Port: 47298,
Typ: ICECandidateTypeSrflx,
Component: 1,
RelatedAddress: "203.0.113.1",
RelatedPort: 5000,
},
}, {
// Chrome/Webkit-style ICECandidate:
`{"foundation":"1966762134", "component":"rtp", "protocol":"udp", "priority":2122260223, "address":"192.168.20.129", "port":47299, "type":"host", "relatedAddress":null}`,
ICECandidate{
Foundation: "1966762134",
Priority: 2122260223,
Address: "192.168.20.129",
Protocol: ICEProtocolUDP,
Port: 47299,
Typ: ICECandidateTypeHost,
Component: 1,
RelatedAddress: "<null>",
RelatedPort: 0,
},
}, {
// Both are present, Chrome/Webkit-style takes precedent:
`{"candidate":"1966762133 1 udp 2122260222 192.168.20.128 47298 typ srflx raddr 203.0.113.1 rport 5000", "foundation":"1966762134", "component":"rtp", "protocol":"udp", "priority":2122260223, "address":"192.168.20.129", "port":47299, "type":"host", "relatedAddress":null}`,
ICECandidate{
Foundation: "1966762134",
Priority: 2122260223,
Address: "192.168.20.129",
Protocol: ICEProtocolUDP,
Port: 47299,
Typ: ICECandidateTypeHost,
Component: 1,
RelatedAddress: "<null>",
RelatedPort: 0,
},
},
}

for i, testCase := range testCases {
v := map[string]interface{}{}
err := json.Unmarshal([]byte(testCase.jsonCandidate), &v)
if err != nil {
t.Errorf("Case %d: bad test, got error: %v", i, err)
}
assert.Equal(t, testCase.expect, *valueToICECandidate(js.ValueOf(v)))
}
}

0 comments on commit 7a95bb8

Please sign in to comment.