mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 07:06:51 +08:00
Add ICE candidate event handlers
Add OnICECandidate and OnICEGatheringStateChange methods to PeerConnection. The main goal of this change is to improve API compatibility with the JavaScript/Wasm bindings. It does not actually add trickle ICE support or change the ICE candidate gathering process, which is still synchronous in the Go implementation. Rather, it fires the appropriate events similar to they way they would be fired in a true trickle ICE process. Remove unused OnNegotiationNeeded event handler. This handler is not required for most applications and would be difficult to implement in Go. This commit removes the handler from the JavaScript/Wasm bindings, which leads to a more similar API for Go and JavaScript/Wasm. Add OnICEGatheringStateChange to the JavaScript/Wasm bindings. Also changes the Go implementation so that the function signatures match.
This commit is contained in:
@@ -158,6 +158,12 @@ func TestDataChannelParamters_Go(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
answerPC.OnDataChannel(func(d *DataChannel) {
|
answerPC.OnDataChannel(func(d *DataChannel) {
|
||||||
|
// Make sure this is the data channel we were looking for. (Not the one
|
||||||
|
// created in signalPair).
|
||||||
|
if d.Label() != expectedLabel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if parameters are correctly set
|
// Check if parameters are correctly set
|
||||||
assert.True(t, d.ordered, "Ordered should be set to true")
|
assert.True(t, d.ordered, "Ordered should be set to true")
|
||||||
if assert.NotNil(t, d.maxPacketLifeTime, "should not be nil") {
|
if assert.NotNil(t, d.maxPacketLifeTime, "should not be nil") {
|
||||||
|
@@ -39,24 +39,26 @@ func main() {
|
|||||||
log(fmt.Sprintf("Message from DataChannel %s payload %s", sendChannel.Label(), string(msg.Data)))
|
log(fmt.Sprintf("Message from DataChannel %s payload %s", sendChannel.Label(), string(msg.Data)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Create offer
|
||||||
|
offer, err := pc.CreateOffer(nil)
|
||||||
|
if err != nil {
|
||||||
|
handleError(err)
|
||||||
|
}
|
||||||
|
if err := pc.SetLocalDescription(offer); err != nil {
|
||||||
|
handleError(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Add handlers for setting up the connection.
|
// Add handlers for setting up the connection.
|
||||||
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
||||||
log(fmt.Sprint(state))
|
log(fmt.Sprint(state))
|
||||||
})
|
})
|
||||||
pc.OnICECandidate(func(candidate *string) {
|
pc.OnICECandidate(func(candidate *webrtc.ICECandidate) {
|
||||||
if candidate != nil {
|
if candidate != nil {
|
||||||
encodedDescr := signal.Encode(pc.LocalDescription())
|
encodedDescr := signal.Encode(pc.LocalDescription())
|
||||||
el := getElementByID("localSessionDescription")
|
el := getElementByID("localSessionDescription")
|
||||||
el.Set("value", encodedDescr)
|
el.Set("value", encodedDescr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
pc.OnNegotiationNeeded(func() {
|
|
||||||
offer, err := pc.CreateOffer(nil)
|
|
||||||
if err != nil {
|
|
||||||
handleError(err)
|
|
||||||
}
|
|
||||||
pc.SetLocalDescription(offer)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Set up global callbacks which will be triggered on button clicks.
|
// Set up global callbacks which will be triggered on button clicks.
|
||||||
js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} {
|
js.Global().Set("sendMessage", js.FuncOf(func(_ js.Value, _ []js.Value) interface{} {
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pions/webrtc"
|
"github.com/pions/webrtc"
|
||||||
|
|
||||||
"github.com/pions/webrtc/examples/internal/signal"
|
"github.com/pions/webrtc/examples/internal/signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
14
js_utils.go
14
js_utils.go
@@ -92,6 +92,20 @@ func valueToUint8OrZero(val js.Value) uint8 {
|
|||||||
return uint8(val.Int())
|
return uint8(val.Int())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func valueToUint16OrZero(val js.Value) uint16 {
|
||||||
|
if val == js.Null() || val == js.Undefined() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint16(val.Int())
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueToUint32OrZero(val js.Value) uint32 {
|
||||||
|
if val == js.Null() || val == js.Undefined() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return uint32(val.Int())
|
||||||
|
}
|
||||||
|
|
||||||
func valueToStrings(val js.Value) []string {
|
func valueToStrings(val js.Value) []string {
|
||||||
result := make([]string, val.Length())
|
result := make([]string, val.Length())
|
||||||
for i := 0; i < val.Length(); i++ {
|
for i := 0; i < val.Length(); i++ {
|
||||||
|
@@ -35,7 +35,7 @@ type PeerConnection struct {
|
|||||||
currentRemoteDescription *SessionDescription
|
currentRemoteDescription *SessionDescription
|
||||||
pendingRemoteDescription *SessionDescription
|
pendingRemoteDescription *SessionDescription
|
||||||
signalingState SignalingState
|
signalingState SignalingState
|
||||||
iceGatheringState ICEGatheringState // FIXME NOT-USED
|
iceGatheringState ICEGatheringState
|
||||||
iceConnectionState ICEConnectionState
|
iceConnectionState ICEConnectionState
|
||||||
connectionState PeerConnectionState
|
connectionState PeerConnectionState
|
||||||
|
|
||||||
@@ -53,16 +53,16 @@ type PeerConnection struct {
|
|||||||
dataChannels map[uint16]*DataChannel
|
dataChannels map[uint16]*DataChannel
|
||||||
|
|
||||||
// OnNegotiationNeeded func() // FIXME NOT-USED
|
// OnNegotiationNeeded func() // FIXME NOT-USED
|
||||||
// OnICECandidate func() // FIXME NOT-USED
|
|
||||||
// OnICECandidateError func() // FIXME NOT-USED
|
// OnICECandidateError func() // FIXME NOT-USED
|
||||||
|
|
||||||
// OnICEGatheringStateChange func() // FIXME NOT-USED
|
|
||||||
// OnConnectionStateChange func() // FIXME NOT-USED
|
// OnConnectionStateChange func() // FIXME NOT-USED
|
||||||
|
|
||||||
onSignalingStateChangeHandler func(SignalingState)
|
onSignalingStateChangeHandler func(SignalingState)
|
||||||
onICEConnectionStateChangeHandler func(ICEConnectionState)
|
onICEConnectionStateChangeHandler func(ICEConnectionState)
|
||||||
onTrackHandler func(*Track, *RTPReceiver)
|
onTrackHandler func(*Track, *RTPReceiver)
|
||||||
onDataChannelHandler func(*DataChannel)
|
onDataChannelHandler func(*DataChannel)
|
||||||
|
onICECandidateHandler func(*ICECandidate)
|
||||||
|
onICEGatheringStateChangeHandler func()
|
||||||
|
|
||||||
iceGatherer *ICEGatherer
|
iceGatherer *ICEGatherer
|
||||||
iceTransport *ICETransport
|
iceTransport *ICETransport
|
||||||
@@ -238,6 +238,61 @@ func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
|
|||||||
pc.onDataChannelHandler = f
|
pc.onDataChannelHandler = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnICECandidate sets an event handler which is invoked when a new ICE
|
||||||
|
// candidate is found.
|
||||||
|
// BUG: trickle ICE is not supported so this event is triggered immediately when
|
||||||
|
// SetLocalDescription is called. Typically, you only need to use this method
|
||||||
|
// if you want API compatibility with the JavaScript/Wasm bindings.
|
||||||
|
func (pc *PeerConnection) OnICECandidate(f func(*ICECandidate)) {
|
||||||
|
pc.mu.Lock()
|
||||||
|
defer pc.mu.Unlock()
|
||||||
|
pc.onICECandidateHandler = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnICEGatheringStateChange sets an event handler which is invoked when the
|
||||||
|
// ICE candidate gathering state has changed.
|
||||||
|
// BUG: trickle ICE is not supported so this event is triggered immediately when
|
||||||
|
// SetLocalDescription is called. Typically, you only need to use this method
|
||||||
|
// if you want API compatibility with the JavaScript/Wasm bindings.
|
||||||
|
func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
|
||||||
|
pc.mu.Lock()
|
||||||
|
defer pc.mu.Unlock()
|
||||||
|
pc.onICEGatheringStateChangeHandler = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// signalICECandidateGatheringComplete should be called after ICE candidate
|
||||||
|
// gathering is complete. It triggers the appropriate event handlers in order to
|
||||||
|
// emulate a trickle ICE process.
|
||||||
|
func (pc *PeerConnection) signalICECandidateGatheringComplete() error {
|
||||||
|
pc.mu.Lock()
|
||||||
|
defer pc.mu.Unlock()
|
||||||
|
|
||||||
|
// Call onICECandidateHandler for all candidates.
|
||||||
|
if pc.onICECandidateHandler != nil {
|
||||||
|
candidates, err := pc.iceGatherer.GetLocalCandidates()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range candidates {
|
||||||
|
go pc.onICECandidateHandler(&candidates[i])
|
||||||
|
}
|
||||||
|
// Call the handler one last time with nil. This is a signal that candidate
|
||||||
|
// gathering is complete.
|
||||||
|
go pc.onICECandidateHandler(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
pc.iceGatheringState = ICEGatheringStateComplete
|
||||||
|
|
||||||
|
// Also trigger the onICEGatheringStateChangeHandler
|
||||||
|
if pc.onICEGatheringStateChangeHandler != nil {
|
||||||
|
// Note: Gathering is already done at this point, but some clients might
|
||||||
|
// still expect the state change handler to be triggered.
|
||||||
|
go pc.onICEGatheringStateChangeHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// OnTrack sets an event handler which is called when remote track
|
// OnTrack sets an event handler which is called when remote track
|
||||||
// arrives from a remote peer.
|
// arrives from a remote peer.
|
||||||
func (pc *PeerConnection) OnTrack(f func(*Track, *RTPReceiver)) {
|
func (pc *PeerConnection) OnTrack(f func(*Track, *RTPReceiver)) {
|
||||||
@@ -687,7 +742,18 @@ func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error {
|
|||||||
if err := desc.parsed.Unmarshal([]byte(desc.SDP)); err != nil {
|
if err := desc.parsed.Unmarshal([]byte(desc.SDP)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return pc.setDescription(&desc, stateChangeOpSetLocal)
|
if err := pc.setDescription(&desc, stateChangeOpSetLocal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the appropriate event handlers to signal that ICE candidate gathering
|
||||||
|
// is complete. In reality it completed a while ago, but triggering these
|
||||||
|
// events helps maintain API compatibility with the JavaScript/Wasm bindings.
|
||||||
|
if err := pc.signalICECandidateGatheringComplete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalDescription returns pendingLocalDescription if it is not null and
|
// LocalDescription returns pendingLocalDescription if it is not null and
|
||||||
@@ -1510,7 +1576,6 @@ func (pc *PeerConnection) SignalingState() SignalingState {
|
|||||||
|
|
||||||
// ICEGatheringState attribute returns the ICE gathering state of the
|
// ICEGatheringState attribute returns the ICE gathering state of the
|
||||||
// PeerConnection instance.
|
// PeerConnection instance.
|
||||||
// FIXME NOT-USED
|
|
||||||
func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
|
func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
|
||||||
return pc.iceGatheringState
|
return pc.iceGatheringState
|
||||||
}
|
}
|
||||||
|
@@ -34,39 +34,6 @@ func (api *API) newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, er
|
|||||||
|
|
||||||
return pca, pcb, nil
|
return pca, pcb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
|
|
||||||
offer, err := pcOffer.CreateOffer(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pcOffer.SetLocalDescription(offer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcAnswer.SetRemoteDescription(offer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
answer, err := pcAnswer.CreateAnswer(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcOffer.SetRemoteDescription(answer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew_Go(t *testing.T) {
|
func TestNew_Go(t *testing.T) {
|
||||||
api := NewAPI()
|
api := NewAPI()
|
||||||
t.Run("Success", func(t *testing.T) {
|
t.Run("Success", func(t *testing.T) {
|
||||||
|
@@ -22,7 +22,7 @@ type PeerConnection struct {
|
|||||||
onDataChannelHandler *js.Func
|
onDataChannelHandler *js.Func
|
||||||
onICEConectionStateChangeHandler *js.Func
|
onICEConectionStateChangeHandler *js.Func
|
||||||
onICECandidateHandler *js.Func
|
onICECandidateHandler *js.Func
|
||||||
onNegotiationNeededHandler *js.Func
|
onICEGatheringStateChangeHandler *js.Func
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeerConnection creates a peerconnection with the default
|
// NewPeerConnection creates a peerconnection with the default
|
||||||
@@ -276,17 +276,15 @@ func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
|
|||||||
return newICEConnectionState(pc.underlying.Get("iceConnectionState").String())
|
return newICEConnectionState(pc.underlying.Get("iceConnectionState").String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(albrow): This function doesn't exist in the Go implementation.
|
// OnICECandidate sets an event handler which is invoked when a new ICE
|
||||||
// TODO(albrow): Follow the spec more closely. Handler should accept
|
// candidate is found.
|
||||||
// RTCPeerConnectionIceEvent instead of *string.
|
func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onicecandidate
|
|
||||||
func (pc *PeerConnection) OnICECandidate(f func(candidate *string)) {
|
|
||||||
if pc.onICECandidateHandler != nil {
|
if pc.onICECandidateHandler != nil {
|
||||||
oldHandler := pc.onICECandidateHandler
|
oldHandler := pc.onICECandidateHandler
|
||||||
defer oldHandler.Release()
|
defer oldHandler.Release()
|
||||||
}
|
}
|
||||||
onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||||
candidate := valueToStringPointer(args[0].Get("candidate"))
|
candidate := valueToICECandidate(args[0].Get("candidate"))
|
||||||
go f(candidate)
|
go f(candidate)
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
})
|
})
|
||||||
@@ -294,18 +292,19 @@ func (pc *PeerConnection) OnICECandidate(f func(candidate *string)) {
|
|||||||
pc.underlying.Set("onicecandidate", onICECandidateHandler)
|
pc.underlying.Set("onicecandidate", onICECandidateHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(albrow): This function doesn't exist in the Go implementation.
|
// OnICEGatheringStateChange sets an event handler which is invoked when the
|
||||||
func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
|
// ICE candidate gathering state has changed.
|
||||||
if pc.onNegotiationNeededHandler != nil {
|
func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
|
||||||
oldHandler := pc.onNegotiationNeededHandler
|
if pc.onICEGatheringStateChangeHandler != nil {
|
||||||
|
oldHandler := pc.onICEGatheringStateChangeHandler
|
||||||
defer oldHandler.Release()
|
defer oldHandler.Release()
|
||||||
}
|
}
|
||||||
onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||||
go f()
|
go f()
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
})
|
})
|
||||||
pc.onNegotiationNeededHandler = &onNegotiationNeededHandler
|
pc.onICEGatheringStateChangeHandler = &onICEGatheringStateChangeHandler
|
||||||
pc.underlying.Set("onnegotiationneeded", onNegotiationNeededHandler)
|
pc.underlying.Set("onicegatheringstatechange", onICEGatheringStateChangeHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// // GetSenders returns the RTPSender that are currently attached to this PeerConnection
|
// // GetSenders returns the RTPSender that are currently attached to this PeerConnection
|
||||||
@@ -384,8 +383,8 @@ func (pc *PeerConnection) Close() (err error) {
|
|||||||
if pc.onICECandidateHandler != nil {
|
if pc.onICECandidateHandler != nil {
|
||||||
pc.onICECandidateHandler.Release()
|
pc.onICECandidateHandler.Release()
|
||||||
}
|
}
|
||||||
if pc.onNegotiationNeededHandler != nil {
|
if pc.onICEGatheringStateChangeHandler != nil {
|
||||||
pc.onNegotiationNeededHandler.Release()
|
pc.onICEGatheringStateChangeHandler.Release()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -529,6 +528,36 @@ func valueToICEServer(iceServerValue js.Value) ICEServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func valueToICECandidate(val js.Value) *ICECandidate {
|
||||||
|
if val == js.Null() || val == js.Undefined() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
protocol, _ := newICEProtocol(val.Get("protocol").String())
|
||||||
|
candidateType, _ := newICECandidateType(val.Get("type").String())
|
||||||
|
return &ICECandidate{
|
||||||
|
Foundation: val.Get("foundation").String(),
|
||||||
|
Priority: valueToUint32OrZero(val.Get("priority")),
|
||||||
|
IP: val.Get("ip").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 {
|
func sessionDescriptionToValue(desc *SessionDescription) js.Value {
|
||||||
if desc == nil {
|
if desc == nil {
|
||||||
return js.Undefined()
|
return js.Undefined()
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
// +build js,wasm
|
|
||||||
|
|
||||||
package webrtc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) (err error) {
|
|
||||||
offerChan := make(chan SessionDescription)
|
|
||||||
pcOffer.OnICECandidate(func(candidate *string) {
|
|
||||||
if candidate == nil {
|
|
||||||
offerChan <- *pcOffer.PendingLocalDescription()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Note(albrow): We need to create a data channel in order to trigger ICE
|
|
||||||
// candidate gathering in the background.
|
|
||||||
if _, err := pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
offer, err := pcOffer.CreateOffer(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := pcOffer.SetLocalDescription(offer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout := time.After(3 * time.Second)
|
|
||||||
select {
|
|
||||||
case <-timeout:
|
|
||||||
return fmt.Errorf("timed out waiting to receive offer")
|
|
||||||
case offer := <-offerChan:
|
|
||||||
if err := pcAnswer.SetRemoteDescription(offer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
answer, err := pcAnswer.CreateAnswer(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = pcOffer.SetRemoteDescription(answer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,6 +1,7 @@
|
|||||||
package webrtc
|
package webrtc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -26,6 +27,56 @@ func newPair() (pcOffer *PeerConnection, pcAnswer *PeerConnection, err error) {
|
|||||||
return pca, pcb, nil
|
return pca, pcb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func signalPair(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
|
||||||
|
offerChan := make(chan SessionDescription)
|
||||||
|
pcOffer.OnICECandidate(func(candidate *ICECandidate) {
|
||||||
|
if candidate == nil {
|
||||||
|
offerChan <- *pcOffer.PendingLocalDescription()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Note(albrow): We need to create a data channel in order to trigger ICE
|
||||||
|
// candidate gathering in the background for the JavaScript/Wasm bindings. If
|
||||||
|
// we don't do this, the complete offer including ICE candidates will never be
|
||||||
|
// generated.
|
||||||
|
if _, err := pcOffer.CreateDataChannel("initial_data_channel", nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
offer, err := pcOffer.CreateOffer(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := pcOffer.SetLocalDescription(offer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := time.After(3 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return fmt.Errorf("timed out waiting to receive offer")
|
||||||
|
case offer := <-offerChan:
|
||||||
|
if err := pcAnswer.SetRemoteDescription(offer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
answer, err := pcAnswer.CreateAnswer(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = pcAnswer.SetLocalDescription(answer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pcOffer.SetRemoteDescription(answer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
pc, err := NewPeerConnection(Configuration{
|
pc, err := NewPeerConnection(Configuration{
|
||||||
ICEServers: []ICEServer{
|
ICEServers: []ICEServer{
|
||||||
|
Reference in New Issue
Block a user