Files
webrtc/rtcpeerconnection.go
Sean DuBois 1be721e5fa ICE works again, with proper agent implementation
Candidates are now generated with real priority, and actually pay
attention to the inbound messages.

Next we just need to pluck use-candidate + ice-controlling from the
messages and we will have a full-ice implementation (but we are not
controlling)

We then need to add timers to alert when the peer goes away OR when we
need to send again.
2018-08-11 13:56:28 -07:00

219 lines
5.9 KiB
Go

package webrtc
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/pions/webrtc/internal/network"
"github.com/pions/webrtc/internal/sdp"
"github.com/pions/webrtc/pkg/ice"
"github.com/pions/webrtc/pkg/rtp"
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// RTCPeerConnectionState indicates the state of the peer connection
type RTCPeerConnectionState int
const (
// RTCPeerConnectionStateNew indicates some of the ICE or DTLS transports are in status new
RTCPeerConnectionStateNew RTCPeerConnectionState = iota + 1
// RTCPeerConnectionStateConnecting indicates some of the ICE or DTLS transports are in status connecting or checking
RTCPeerConnectionStateConnecting
// RTCPeerConnectionStateConnected indicates all of the ICE or DTLS transports are in status connected or completed
RTCPeerConnectionStateConnected
// RTCPeerConnectionStateDisconnected indicates some of the ICE or DTLS transports are in status disconnected
RTCPeerConnectionStateDisconnected
// RTCPeerConnectionStateFailed indicates some of the ICE or DTLS transports are in status failed
RTCPeerConnectionStateFailed
// RTCPeerConnectionStateClosed indicates the peer connection is closed
RTCPeerConnectionStateClosed
)
func (t RTCPeerConnectionState) String() string {
switch t {
case RTCPeerConnectionStateNew:
return "new"
case RTCPeerConnectionStateConnecting:
return "connecting"
case RTCPeerConnectionStateConnected:
return "connected"
case RTCPeerConnectionStateDisconnected:
return "disconnected"
case RTCPeerConnectionStateFailed:
return "failed"
case RTCPeerConnectionStateClosed:
return "closed"
default:
return "Unknown"
}
}
// RTCPeerConnection represents a WebRTC connection between itself and a remote peer
type RTCPeerConnection struct {
sync.RWMutex
// ICE
OnICEConnectionStateChange func(iceConnectionState ice.ConnectionState)
config RTCConfiguration
networkManager *network.Manager
// Signaling
// pendingLocalDescription *RTCSessionDescription
// currentLocalDescription *RTCSessionDescription
LocalDescription *sdp.SessionDescription
// pendingRemoteDescription *RTCSessionDescription
currentRemoteDescription *RTCSessionDescription
remoteDescription *sdp.SessionDescription
idpLoginURL *string
IsClosed bool
NegotiationNeeded bool
// lastOffer string
// lastAnswer string
signalingState RTCSignalingState
connectionState RTCPeerConnectionState
// Media
mediaEngine *MediaEngine
rtpTransceivers []*RTCRtpTransceiver
Ontrack func(*RTCTrack)
// DataChannels
dataChannels map[uint16]*RTCDataChannel
Ondatachannel func(*RTCDataChannel)
}
// Public
// New creates a new RTCPeerConfiguration with the provided configuration
func New(config RTCConfiguration) (*RTCPeerConnection, error) {
r := &RTCPeerConnection{
config: config,
signalingState: RTCSignalingStateStable,
connectionState: RTCPeerConnectionStateNew,
mediaEngine: DefaultMediaEngine,
dataChannels: make(map[uint16]*RTCDataChannel),
}
var err error
r.networkManager, err = network.NewManager(r.generateChannel, r.dataChannelEventHandler, r.iceStateChange)
if err != nil {
return nil, err
}
if err := r.SetConfiguration(config); err != nil {
return nil, err
}
return r, nil
}
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
// This enables RTCPeerConnection with support for different codecs
func (r *RTCPeerConnection) SetMediaEngine(m *MediaEngine) {
r.mediaEngine = m
}
// SetIdentityProvider is used to configure an identity provider to generate identity assertions
func (r *RTCPeerConnection) SetIdentityProvider(provider string) error {
panic("TODO SetIdentityProvider")
}
// Close ends the RTCPeerConnection
func (r *RTCPeerConnection) Close() error {
r.networkManager.Close()
return nil
}
/* Everything below is private */
func (r *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buffers chan<- *rtp.Packet) {
if r.Ontrack == nil {
return nil
}
sdpCodec, err := r.remoteDescription.GetCodecForPayloadType(payloadType)
if err != nil {
fmt.Printf("No codec could be found in RemoteDescription for payloadType %d \n", payloadType)
return nil
}
codec, err := r.mediaEngine.getCodecSDP(sdpCodec)
if err != nil {
fmt.Printf("Codec %s in not registered\n", sdpCodec)
}
bufferTransport := make(chan *rtp.Packet, 15)
track := &RTCTrack{
PayloadType: payloadType,
Kind: codec.Type,
ID: "0", // TODO extract from remoteDescription
Label: "", // TODO extract from remoteDescription
Ssrc: ssrc,
Codec: codec,
Packets: bufferTransport,
}
// TODO: Register the receiving Track
go r.Ontrack(track)
return bufferTransport
}
func (r *RTCPeerConnection) iceStateChange(newState ice.ConnectionState) {
r.Lock()
defer r.Unlock()
// if r.OnICEConnectionStateChange != nil && r.iceState != newState {
// r.OnICEConnectionStateChange(newState)
// }
// r.iceState = newState
}
func (r *RTCPeerConnection) dataChannelEventHandler(e network.DataChannelEvent) {
r.Lock()
defer r.Unlock()
switch event := e.(type) {
case *network.DataChannelCreated:
newDataChannel := &RTCDataChannel{ID: event.StreamIdentifier(), Label: event.Label, rtcPeerConnection: r}
r.dataChannels[e.StreamIdentifier()] = newDataChannel
if r.Ondatachannel != nil {
go r.Ondatachannel(newDataChannel)
} else {
fmt.Println("Ondatachannel is unset, discarding message")
}
case *network.DataChannelMessage:
if datachannel, ok := r.dataChannels[e.StreamIdentifier()]; ok {
datachannel.RLock()
defer datachannel.RUnlock()
if datachannel.Onmessage != nil {
go datachannel.Onmessage(event.Payload)
} else {
fmt.Printf("Onmessage has not been set for Datachannel %s %d \n", datachannel.Label, e.StreamIdentifier())
}
} else {
fmt.Printf("No datachannel found for streamIdentifier %d \n", e.StreamIdentifier())
}
default:
fmt.Printf("Unhandled DataChannelEvent %v \n", event)
}
}