Change the names of event handlers and attributes for readability

This commit is contained in:
Konstantin Itskov
2018-08-29 17:05:26 -04:00
parent 4502076147
commit f738cec9da
10 changed files with 422 additions and 250 deletions

View File

@@ -33,7 +33,7 @@ func main() {
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
// TODO: find the correct place for this

View File

@@ -54,14 +54,14 @@ func main() {
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
}
datachannels := make([]*webrtc.RTCDataChannel, 0)
var dataChannelsLock sync.RWMutex
peerConnection.Ondatachannel = func(d *webrtc.RTCDataChannel) {
peerConnection.OnDataChannel = func(d *webrtc.RTCDataChannel) {
dataChannelsLock.Lock()
datachannels = append(datachannels, d)
dataChannelsLock.Unlock()

View File

@@ -46,7 +46,7 @@ func main() {
// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
// for the given codec
peerConnection.Ontrack = func(track *webrtc.RTCTrack) {
peerConnection.OnTrack = func(track *webrtc.RTCTrack) {
codec := track.Codec
fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType, codec.Name)
pipeline := gst.CreatePipeline(codec.Name)
@@ -59,7 +59,7 @@ func main() {
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
}

View File

@@ -46,7 +46,7 @@ func main() {
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
}

View File

@@ -43,7 +43,7 @@ func buildPeerConnection() *webrtc.RTCPeerConnection {
}
d.Unlock()
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
if connectionState == ice.ConnectionStateConnected {
fmt.Println("sending openchannel")

View File

@@ -22,11 +22,11 @@ func buildPeerConnection() *webrtc.RTCPeerConnection {
panic(err)
}
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
}
peerConnection.Ondatachannel = func(d *webrtc.RTCDataChannel) {
peerConnection.OnDataChannel = func(d *webrtc.RTCDataChannel) {
fmt.Printf("New DataChannel %s %d\n", d.Label, d.ID)
d.Lock()

View File

@@ -46,7 +46,7 @@ func main() {
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
// an ivf file, since we could have multiple video tracks we provide a counter.
// In your application this is where you would handle/process video
peerConnection.Ontrack = func(track *webrtc.RTCTrack) {
peerConnection.OnTrack = func(track *webrtc.RTCTrack) {
if track.Codec.Name == webrtc.VP8 {
fmt.Println("Got VP8 track, saving to disk as output.ivf")
i, err := newIVFWriter("output.ivf")
@@ -61,7 +61,7 @@ func main() {
// Set the handler for ICE connection state
// This will notify you when the peer has connected/disconnected
peerConnection.OnICEConnectionStateChange = func(connectionState ice.ConnectionState) {
peerConnection.OnIceConnectionStateChange = func(connectionState ice.ConnectionState) {
fmt.Printf("Connection State has changed %s \n", connectionState.String())
}

View File

@@ -35,7 +35,7 @@ const (
RTCIceConnectionStateFailed
// RTCIceConnectionStateClosed indicates that the RTCPeerConnection's
// IsClosed is true.
// isClosed is true.
RTCIceConnectionStateClosed
)

View File

@@ -11,6 +11,7 @@ import (
"sync"
"time"
"encoding/binary"
"github.com/pions/webrtc/internal/network"
"github.com/pions/webrtc/internal/sdp"
"github.com/pions/webrtc/pkg/ice"
@@ -23,12 +24,12 @@ import (
// comparisons when no value was defined.
const Unknown = iota
// RTCPeerConnection represents a WebRTC connection between itself and a remote peer
// RTCPeerConnection represents a WebRTC connection that establishes a
// peer-to-peer communications with another RTCPeerConnection instance in a
// browser, or to another endpoint implementing the required protocols.
type RTCPeerConnection struct {
sync.RWMutex
networkManager *network.Manager
configuration RTCConfiguration
// CurrentLocalDescription represents the local description that was
@@ -56,25 +57,36 @@ type RTCPeerConnection struct {
// null.
PendingRemoteDescription *RTCSessionDescription
// IceConnectionState attribute MUST return the ICE connection state of the
// SignalingState attribute returns the signaling state of the
// RTCPeerConnection instance.
IceConnectionState RTCIceConnectionState
SignalingState RTCSignalingState
// IceGatheringState attribute returns the ICE gathering state of the
// RTCPeerConnection instance.
IceGatheringState RTCIceGatheringState // FIXME NOT-USED
// IceConnectionState attribute returns the ICE connection state of the
// RTCPeerConnection instance.
// IceConnectionState RTCIceConnectionState
IceConnectionState ice.ConnectionState // FIXME REMOVE
// ConnectionState attribute returns the connection state of the
// RTCPeerConnection instance.
ConnectionState RTCPeerConnectionState
networkManager *network.Manager
idpLoginURL *string
IsClosed bool
NegotiationNeeded bool
isClosed bool
negotiationNeeded bool // FIXME NOT-USED
// lastOffer string
// lastAnswer string
signalingState RTCSignalingState
connectionState RTCPeerConnectionState
// Media
mediaEngine *MediaEngine
rtpTransceivers []*RTCRtpTransceiver
Ontrack func(*RTCTrack)
// SCTP
sctp *RTCSctpTransport
@@ -82,8 +94,15 @@ type RTCPeerConnection struct {
// DataChannels
dataChannels map[uint16]*RTCDataChannel
OnICEConnectionStateChange func(ice.ConnectionState)
Ondatachannel func(*RTCDataChannel)
// OnNegotiationNeeded func()
// OnIceCandidate func()
// OnIceCandidateError func()
// OnSignalingStateChange func()
OnIceConnectionStateChange func(ice.ConnectionState)
// OnIceGatheringStateChange func()
// OnConnectionStateChange func()
OnTrack func(*RTCTrack)
OnDataChannel func(*RTCDataChannel)
}
// Public
@@ -101,8 +120,8 @@ func New(configuration RTCConfiguration) (*RTCPeerConnection, error) {
Certificates: []RTCCertificate{},
IceCandidatePoolSize: 0,
},
signalingState: RTCSignalingStateStable,
connectionState: RTCPeerConnectionStateNew,
SignalingState: RTCSignalingStateStable,
ConnectionState: RTCPeerConnectionStateNew,
mediaEngine: DefaultMediaEngine,
sctp: newRTCSctpTransport(),
dataChannels: make(map[uint16]*RTCDataChannel),
@@ -201,7 +220,7 @@ func (pc *RTCPeerConnection) initConfiguration(configuration RTCConfiguration) e
// SetConfiguration updates the configuration of this RTCPeerConnection object.
func (pc *RTCPeerConnection) SetConfiguration(configuration RTCConfiguration) error {
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
if pc.IsClosed {
if pc.isClosed {
return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
@@ -279,6 +298,112 @@ func (pc *RTCPeerConnection) GetConfiguration() RTCConfiguration {
return pc.configuration
}
// ------------------------------------------------------------------------
// --- FIXME - BELOW CODE NEEDS REVIEW/CLEANUP
// ------------------------------------------------------------------------
// CreateOffer starts the RTCPeerConnection and generates the localDescription
func (pc *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDescription, error) {
useIdentity := pc.idpLoginURL != nil
if options != nil {
return RTCSessionDescription{}, errors.Errorf("TODO handle options")
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.isClosed {
return RTCSessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
candidates := pc.generateLocalCandidates()
bundleValue := "BUNDLE"
if pc.addRTPMediaSection(d, RTCRtpCodecTypeAudio, "audio", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
bundleValue += " audio"
}
if pc.addRTPMediaSection(d, RTCRtpCodecTypeVideo, "video", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
bundleValue += " video"
}
pc.addDataMediaSection(d, "data", candidates, sdp.ConnectionRoleActpass)
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue+" data")
for _, m := range d.MediaDescriptions {
m.WithPropertyAttribute("setup:actpass")
}
pc.CurrentLocalDescription = &RTCSessionDescription{
Type: RTCSdpTypeOffer,
Sdp: d.Marshal(),
parsed: d,
}
return *pc.CurrentLocalDescription, nil
}
// CreateAnswer starts the RTCPeerConnection and generates the localDescription
func (pc *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSessionDescription, error) {
useIdentity := pc.idpLoginURL != nil
if options != nil {
return RTCSessionDescription{}, errors.Errorf("TODO handle options")
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.isClosed {
return RTCSessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
candidates := pc.generateLocalCandidates()
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
bundleValue := "BUNDLE"
for _, remoteMedia := range pc.CurrentRemoteDescription.parsed.MediaDescriptions {
// TODO @trivigy better SDP parser
var peerDirection RTCRtpTransceiverDirection
midValue := ""
for _, a := range remoteMedia.Attributes {
if strings.HasPrefix(*a.String(), "mid") {
midValue = (*a.String())[len("mid:"):]
} else if strings.HasPrefix(*a.String(), "sendrecv") {
peerDirection = RTCRtpTransceiverDirectionSendrecv
} else if strings.HasPrefix(*a.String(), "sendonly") {
peerDirection = RTCRtpTransceiverDirectionSendonly
} else if strings.HasPrefix(*a.String(), "recvonly") {
peerDirection = RTCRtpTransceiverDirectionRecvonly
}
}
appendBundle := func() {
bundleValue += " " + midValue
}
if strings.HasPrefix(*remoteMedia.MediaName.String(), "audio") {
if pc.addRTPMediaSection(d, RTCRtpCodecTypeAudio, midValue, peerDirection, candidates, sdp.ConnectionRoleActive) {
appendBundle()
}
} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "video") {
if pc.addRTPMediaSection(d, RTCRtpCodecTypeVideo, midValue, peerDirection, candidates, sdp.ConnectionRoleActive) {
appendBundle()
}
} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "application") {
pc.addDataMediaSection(d, midValue, candidates, sdp.ConnectionRoleActive)
}
}
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)
pc.CurrentLocalDescription = &RTCSessionDescription{
Type: RTCSdpTypeAnswer,
Sdp: d.Marshal(),
parsed: d,
}
return *pc.CurrentLocalDescription, nil
}
// SetLocalDescription
func (pc *RTCPeerConnection) SetLocalDescription() {
panic("not implemented yet") // FIXME NOT-IMPLEMENTED
}
// 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.
@@ -290,129 +415,6 @@ func (pc *RTCPeerConnection) LocalDescription() *RTCSessionDescription {
return pc.CurrentLocalDescription
}
// 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 *RTCPeerConnection) RemoteDescription() *RTCSessionDescription {
if pc.PendingRemoteDescription != nil {
return pc.PendingRemoteDescription
}
return pc.CurrentRemoteDescription
}
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
// This enables RTCPeerConnection with support for different codecs
func (pc *RTCPeerConnection) SetMediaEngine(m *MediaEngine) {
pc.mediaEngine = m
}
// SetIdentityProvider is used to configure an identity provider to generate identity assertions
func (pc *RTCPeerConnection) SetIdentityProvider(provider string) error {
return errors.Errorf("TODO SetIdentityProvider")
}
// Close ends the RTCPeerConnection
func (pc *RTCPeerConnection) Close() error {
pc.networkManager.Close()
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2)
if pc.IsClosed {
return nil
}
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
pc.IsClosed = true
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4)
pc.signalingState = RTCSignalingStateClosed
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11)
pc.IceConnectionState = RTCIceConnectionStateClosed
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #12)
pc.connectionState = RTCPeerConnectionStateClosed
return nil
}
/* Everything below is private */
func (pc *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buffers chan<- *rtp.Packet) {
if pc.Ontrack == nil {
return nil
}
sdpCodec, err := pc.CurrentLocalDescription.parsed.GetCodecForPayloadType(payloadType)
if err != nil {
fmt.Printf("No codec could be found in RemoteDescription for payloadType %d \n", payloadType)
return nil
}
codec, err := pc.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 pc.Ontrack(track)
return bufferTransport
}
func (pc *RTCPeerConnection) iceStateChange(newState ice.ConnectionState) {
pc.Lock()
defer pc.Unlock()
if pc.OnICEConnectionStateChange != nil && pc.IceConnectionState != newState {
pc.OnICEConnectionStateChange(newState)
}
pc.IceConnectionState = newState
}
func (pc *RTCPeerConnection) dataChannelEventHandler(e network.DataChannelEvent) {
pc.Lock()
defer pc.Unlock()
switch event := e.(type) {
case *network.DataChannelCreated:
newDataChannel := &RTCDataChannel{ID: event.StreamIdentifier(), Label: event.Label, rtcPeerConnection: pc}
pc.dataChannels[e.StreamIdentifier()] = newDataChannel
if pc.Ondatachannel != nil {
go pc.Ondatachannel(newDataChannel)
} else {
fmt.Println("Ondatachannel is unset, discarding message")
}
case *network.DataChannelMessage:
if datachannel, ok := pc.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)
}
}
// SetRemoteDescription sets the SessionDescription of the remote peer
func (pc *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) error {
if pc.CurrentRemoteDescription != nil {
@@ -450,6 +452,274 @@ func (pc *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) er
return pc.networkManager.Start(weOffer, remoteUfrag, remotePwd)
}
// 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 *RTCPeerConnection) RemoteDescription() *RTCSessionDescription {
if pc.PendingRemoteDescription != nil {
return pc.PendingRemoteDescription
}
return pc.CurrentRemoteDescription
}
func (pc *RTCPeerConnection) addIceCandidate() {
panic("not implemented yet") // FIXME NOT-IMPLEMENTED
}
// ------------------------------------------------------------------------
// --- FIXME - BELOW CODE NEEDS RE-ORGANIZATION
// ------------------------------------------------------------------------
// GetSenders returns the RTCRtpSender that are currently attached to this RTCPeerConnection
func (pc *RTCPeerConnection) GetSenders() []RTCRtpSender {
result := make([]RTCRtpSender, len(pc.rtpTransceivers))
for i, tranceiver := range pc.rtpTransceivers {
result[i] = *tranceiver.Sender
}
return result
}
// GetReceivers returns the RTCRtpReceivers that are currently attached to this RTCPeerConnection
func (pc *RTCPeerConnection) GetReceivers() []RTCRtpReceiver {
result := make([]RTCRtpReceiver, len(pc.rtpTransceivers))
for i, tranceiver := range pc.rtpTransceivers {
result[i] = *tranceiver.Receiver
}
return result
}
// GetTransceivers returns the RTCRtpTransceiver that are currently attached to this RTCPeerConnection
func (pc *RTCPeerConnection) GetTransceivers() []RTCRtpTransceiver {
result := make([]RTCRtpTransceiver, len(pc.rtpTransceivers))
for i, tranceiver := range pc.rtpTransceivers {
result[i] = *tranceiver
}
return result
}
// AddTrack adds a RTCTrack to the RTCPeerConnection
func (pc *RTCPeerConnection) AddTrack(track *RTCTrack) (*RTCRtpSender, error) {
if pc.isClosed {
return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
for _, transceiver := range pc.rtpTransceivers {
if transceiver.Sender.Track == nil {
continue
}
if track.ID == transceiver.Sender.Track.ID {
return nil, &rtcerr.InvalidAccessError{Err: ErrExistingTrack}
}
}
var transceiver *RTCRtpTransceiver
for _, t := range pc.rtpTransceivers {
if !t.stopped &&
// t.Sender == nil && // TODO: check that the sender has never sent
t.Sender.Track == nil &&
t.Receiver.Track != nil &&
t.Receiver.Track.Kind == track.Kind {
transceiver = t
break
}
}
if transceiver != nil {
if err := transceiver.setSendingTrack(track); err != nil {
return nil, err
}
} else {
var receiver *RTCRtpReceiver
sender := newRTCRtpSender(track)
transceiver = pc.newRTCRtpTransceiver(
receiver,
sender,
RTCRtpTransceiverDirectionSendonly,
)
}
transceiver.Mid = track.Kind.String() // TODO: Mid generation
return transceiver.Sender, nil
}
func (pc *RTCPeerConnection) RemoveTrack() {
panic("not implemented yet") // FIXME NOT-IMPLEMENTED
}
func (pc *RTCPeerConnection) AddTransceiver() RTCRtpTransceiver {
panic("not implemented yet") // FIXME NOT-IMPLEMENTED
}
// CreateDataChannel creates a new RTCDataChannel object with the given label and optitional options.
func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataChannelInit) (*RTCDataChannel, error) {
if pc.isClosed {
return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
if len(label) > 65535 {
return nil, &rtcerr.TypeError{Err: ErrInvalidValue}
}
// Defaults
ordered := true
priority := RTCPriorityTypeLow
negotiated := false
if options != nil {
ordered = options.Ordered
priority = options.Priority
negotiated = options.Negotiated
}
var id uint16
if negotiated {
id = options.ID
} else {
var err error
id, err = pc.generateDataChannelID(true) // TODO: base on DTLS role
if err != nil {
return nil, err
}
}
if id > 65534 {
return nil, &rtcerr.TypeError{Err: ErrInvalidValue}
}
if pc.sctp.State == RTCSctpTransportStateConnected &&
id >= pc.sctp.MaxChannels {
return nil, &rtcerr.OperationError{Err: ErrMaxDataChannels}
}
_ = ordered // TODO
_ = priority // TODO
res := &RTCDataChannel{
Label: label,
ID: id,
rtcPeerConnection: pc,
}
// Remember datachannel
pc.dataChannels[id] = res
// Send opening message
// pc.networkManager.SendOpenChannelMessage(id, label)
return res, nil
}
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
// This enables RTCPeerConnection with support for different codecs
func (pc *RTCPeerConnection) SetMediaEngine(m *MediaEngine) {
pc.mediaEngine = m
}
// SetIdentityProvider is used to configure an identity provider to generate identity assertions
func (pc *RTCPeerConnection) SetIdentityProvider(provider string) error {
return errors.Errorf("TODO SetIdentityProvider")
}
// Close ends the RTCPeerConnection
func (pc *RTCPeerConnection) Close() error {
pc.networkManager.Close()
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2)
if pc.isClosed {
return nil
}
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
pc.isClosed = true
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4)
pc.SignalingState = RTCSignalingStateClosed
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11)
// pc.IceConnectionState = RTCIceConnectionStateClosed
pc.IceConnectionState = ice.ConnectionStateClosed // FIXME REMOVE
// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #12)
pc.ConnectionState = RTCPeerConnectionStateClosed
return nil
}
/* Everything below is private */
func (pc *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buffers chan<- *rtp.Packet) {
if pc.Ontrack == nil {
return nil
}
sdpCodec, err := pc.CurrentLocalDescription.parsed.GetCodecForPayloadType(payloadType)
if err != nil {
fmt.Printf("No codec could be found in RemoteDescription for payloadType %d \n", payloadType)
return nil
}
codec, err := pc.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 pc.OnTrack(track)
return bufferTransport
}
func (pc *RTCPeerConnection) iceStateChange(newState ice.ConnectionState) {
pc.Lock()
defer pc.Unlock()
if pc.OnIceConnectionStateChange != nil && pc.IceConnectionState != newState {
pc.OnIceConnectionStateChange(newState)
}
pc.IceConnectionState = newState
}
func (pc *RTCPeerConnection) dataChannelEventHandler(e network.DataChannelEvent) {
pc.Lock()
defer pc.Unlock()
switch event := e.(type) {
case *network.DataChannelCreated:
newDataChannel := &RTCDataChannel{ID: event.StreamIdentifier(), Label: event.Label, rtcPeerConnection: pc}
pc.dataChannels[e.StreamIdentifier()] = newDataChannel
if pc.OnDataChannel != nil {
go pc.OnDataChannel(newDataChannel)
} else {
fmt.Println("OnDataChannel is unset, discarding message")
}
case *network.DataChannelMessage:
if datachannel, ok := pc.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)
}
}
func (pc *RTCPeerConnection) generateLocalCandidates() []string {
pc.networkManager.IceAgent.RLock()
defer pc.networkManager.IceAgent.RUnlock()
@@ -461,104 +731,6 @@ func (pc *RTCPeerConnection) generateLocalCandidates() []string {
return candidates
}
// CreateOffer starts the RTCPeerConnection and generates the localDescription
func (pc *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDescription, error) {
useIdentity := pc.idpLoginURL != nil
if options != nil {
return RTCSessionDescription{}, errors.Errorf("TODO handle options")
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.IsClosed {
return RTCSessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
candidates := pc.generateLocalCandidates()
bundleValue := "BUNDLE"
if pc.addRTPMediaSection(d, RTCRtpCodecTypeAudio, "audio", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
bundleValue += " audio"
}
if pc.addRTPMediaSection(d, RTCRtpCodecTypeVideo, "video", RTCRtpTransceiverDirectionSendrecv, candidates, sdp.ConnectionRoleActpass) {
bundleValue += " video"
}
pc.addDataMediaSection(d, "data", candidates, sdp.ConnectionRoleActpass)
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue+" data")
for _, m := range d.MediaDescriptions {
m.WithPropertyAttribute("setup:actpass")
}
pc.CurrentLocalDescription = &RTCSessionDescription{
Type: RTCSdpTypeOffer,
Sdp: d.Marshal(),
parsed: d,
}
return *pc.CurrentLocalDescription, nil
}
// CreateAnswer starts the RTCPeerConnection and generates the localDescription
func (pc *RTCPeerConnection) CreateAnswer(options *RTCAnswerOptions) (RTCSessionDescription, error) {
useIdentity := pc.idpLoginURL != nil
if options != nil {
return RTCSessionDescription{}, errors.Errorf("TODO handle options")
} else if useIdentity {
return RTCSessionDescription{}, errors.Errorf("TODO handle identity provider")
} else if pc.IsClosed {
return RTCSessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
}
candidates := pc.generateLocalCandidates()
d := sdp.NewJSEPSessionDescription(pc.networkManager.DTLSFingerprint(), useIdentity)
bundleValue := "BUNDLE"
for _, remoteMedia := range pc.CurrentRemoteDescription.parsed.MediaDescriptions {
// TODO @trivigy better SDP parser
var peerDirection RTCRtpTransceiverDirection
midValue := ""
for _, a := range remoteMedia.Attributes {
if strings.HasPrefix(*a.String(), "mid") {
midValue = (*a.String())[len("mid:"):]
} else if strings.HasPrefix(*a.String(), "sendrecv") {
peerDirection = RTCRtpTransceiverDirectionSendrecv
} else if strings.HasPrefix(*a.String(), "sendonly") {
peerDirection = RTCRtpTransceiverDirectionSendonly
} else if strings.HasPrefix(*a.String(), "recvonly") {
peerDirection = RTCRtpTransceiverDirectionRecvonly
}
}
appendBundle := func() {
bundleValue += " " + midValue
}
if strings.HasPrefix(*remoteMedia.MediaName.String(), "audio") {
if pc.addRTPMediaSection(d, RTCRtpCodecTypeAudio, midValue, peerDirection, candidates, sdp.ConnectionRoleActive) {
appendBundle()
}
} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "video") {
if pc.addRTPMediaSection(d, RTCRtpCodecTypeVideo, midValue, peerDirection, candidates, sdp.ConnectionRoleActive) {
appendBundle()
}
} else if strings.HasPrefix(*remoteMedia.MediaName.String(), "application") {
pc.addDataMediaSection(d, midValue, candidates, sdp.ConnectionRoleActive)
appendBundle()
}
}
d = d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue)
pc.CurrentLocalDescription = &RTCSessionDescription{
Type: RTCSdpTypeAnswer,
Sdp: d.Marshal(),
parsed: d,
}
return *pc.CurrentLocalDescription, nil
}
func localDirection(weSend bool, peerDirection RTCRtpTransceiverDirection) RTCRtpTransceiverDirection {
theySend := (peerDirection == RTCRtpTransceiverDirectionSendrecv || peerDirection == RTCRtpTransceiverDirectionSendonly)
if weSend && theySend {

View File

@@ -30,7 +30,7 @@ const (
RTCPeerConnectionStateFailed
// RTCPeerConnectionStateClosed indicates the peer connection is closed
// and the IsClosed member variable of RTCPeerConnection is true.
// and the isClosed member variable of RTCPeerConnection is true.
RTCPeerConnectionStateClosed
)