mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 07:06:51 +08:00
Re-organize CreateDataChannel function and add limited spec compliance
This commit is contained in:
22
errors.go
22
errors.go
@@ -50,10 +50,22 @@ var (
|
|||||||
// IceCandidatePoolSize was made after RTCPeerConnection has been initialized.
|
// IceCandidatePoolSize was made after RTCPeerConnection has been initialized.
|
||||||
ErrModifyingIceCandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
|
ErrModifyingIceCandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
|
||||||
|
|
||||||
// ErrInvalidValue indicates that an invalid value was provided.
|
// ErrStringSizeLimit indicates that the character size limit of string is
|
||||||
ErrInvalidValue = errors.New("invalid value")
|
// exceeded. The limit is hardcoded to 65535 according to specifications.
|
||||||
|
ErrStringSizeLimit = errors.New("data channel label exceeds size limit")
|
||||||
|
|
||||||
// ErrMaxDataChannels indicates that the maximum number of data channels
|
// ErrMaxDataChannelID indicates that the maximum number ID that could be
|
||||||
// was reached.
|
// specified for a data channel has been exceeded.
|
||||||
ErrMaxDataChannels = errors.New("maximum number of datachannels reached")
|
ErrMaxDataChannelID = errors.New("maximum number ID for datachannel specified")
|
||||||
|
|
||||||
|
// ErrNegotiatedWithoutID indicates that an attempt to create a data channel
|
||||||
|
// was made while setting the negotiated option to true without providing
|
||||||
|
// the negotiated channel ID.
|
||||||
|
ErrNegotiatedWithoutID = errors.New("negotiated set without channel id")
|
||||||
|
|
||||||
|
// ErrRetransmitsOrPacketLifeTime indicates that an attempt to create a data
|
||||||
|
// channel was made with both options MaxPacketLifeTime and MaxRetransmits
|
||||||
|
// set together. Such configuration is not supported by the specification
|
||||||
|
// and is mutually exclusive.
|
||||||
|
ErrRetransmitsOrPacketLifeTime = errors.New("both MaxPacketLifeTime and MaxRetransmits was set")
|
||||||
)
|
)
|
||||||
|
@@ -46,9 +46,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the Onmessage to handle incoming messages
|
// Register the OnMessage to handle incoming messages
|
||||||
dataChannel.Lock()
|
dataChannel.Lock()
|
||||||
dataChannel.Onmessage = func(payload datachannel.Payload) {
|
dataChannel.OnMessage = func(payload datachannel.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *datachannel.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), dataChannel.Label, string(p.Data))
|
||||||
|
@@ -70,7 +70,7 @@ func main() {
|
|||||||
|
|
||||||
d.Lock()
|
d.Lock()
|
||||||
defer d.Unlock()
|
defer d.Unlock()
|
||||||
d.Onmessage = func(payload datachannel.Payload) {
|
d.OnMessage = func(payload datachannel.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *datachannel.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
||||||
|
@@ -31,7 +31,7 @@ func buildPeerConnection() *webrtc.RTCPeerConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.Lock()
|
d.Lock()
|
||||||
d.Onmessage = func(payload datachannel.Payload) {
|
d.OnMessage = func(payload datachannel.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *datachannel.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
||||||
|
@@ -31,7 +31,7 @@ func buildPeerConnection() *webrtc.RTCPeerConnection {
|
|||||||
|
|
||||||
d.Lock()
|
d.Lock()
|
||||||
defer d.Unlock()
|
defer d.Unlock()
|
||||||
d.Onmessage = func(payload datachannel.Payload) {
|
d.OnMessage = func(payload datachannel.Payload) {
|
||||||
switch p := payload.(type) {
|
switch p := payload.(type) {
|
||||||
case *datachannel.PayloadString:
|
case *datachannel.PayloadString:
|
||||||
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
fmt.Printf("Message '%s' from DataChannel '%s' payload '%s'\n", p.PayloadType().String(), d.Label, string(p.Data))
|
||||||
|
@@ -13,16 +13,101 @@ import (
|
|||||||
type RTCDataChannel struct {
|
type RTCDataChannel struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
||||||
Onmessage func(datachannel.Payload)
|
// Label represents a label that can be used to distinguish this
|
||||||
ID uint16
|
// RTCDataChannel object from other RTCDataChannel objects. Scripts are
|
||||||
Label string
|
// allowed to create multiple RTCDataChannel objects with the same label.
|
||||||
|
Label string
|
||||||
|
|
||||||
|
// Ordered represents if the RTCDataChannel is ordered, and false if
|
||||||
|
// out-of-order delivery is allowed.
|
||||||
|
Ordered bool
|
||||||
|
|
||||||
|
// MaxPacketLifeTime represents the length of the time window (msec) during
|
||||||
|
// which transmissions and retransmissions may occur in unreliable mode.
|
||||||
|
MaxPacketLifeTime *uint16
|
||||||
|
|
||||||
|
// MaxRetransmits represents the maximum number of retransmissions that are
|
||||||
|
// attempted in unreliable mode.
|
||||||
|
MaxRetransmits *uint16
|
||||||
|
|
||||||
|
// Protocol represents the name of the sub-protocol used with this
|
||||||
|
// RTCDataChannel.
|
||||||
|
Protocol string
|
||||||
|
|
||||||
|
// Negotiated represents whether this RTCDataChannel was negotiated by the
|
||||||
|
// application (true), or not (false).
|
||||||
|
Negotiated bool
|
||||||
|
|
||||||
|
// ID represents the ID for this RTCDataChannel. The value is initally null,
|
||||||
|
// which is what will be returned if the ID was not provided at channel
|
||||||
|
// creation time, and the DTLS role of the SCTP transport has not yet been
|
||||||
|
// negotiated. Otherwise, it will return the ID that was either selected by
|
||||||
|
// the script or generated. After the ID is set to a non-null value, it will
|
||||||
|
// not change.
|
||||||
|
ID *uint16
|
||||||
|
|
||||||
|
// Priority represents the priority for this RTCDataChannel. The priority is
|
||||||
|
// assigned at channel creation time.
|
||||||
|
Priority RTCPriorityType
|
||||||
|
|
||||||
|
// ReadyState represents the state of the RTCDataChannel object.
|
||||||
|
ReadyState RTCDataChannelState
|
||||||
|
|
||||||
|
// BufferedAmount represents the number of bytes of application data
|
||||||
|
// (UTF-8 text and binary data) that have been queued using send(). Even
|
||||||
|
// though the data transmission can occur in parallel, the returned value
|
||||||
|
// MUST NOT be decreased before the current task yielded back to the event
|
||||||
|
// loop to prevent race conditions. The value does not include framing
|
||||||
|
// overhead incurred by the protocol, or buffering done by the operating
|
||||||
|
// system or network hardware. The value of BufferedAmount slot will only
|
||||||
|
// increase with each call to the send() method as long as the ReadyState is
|
||||||
|
// open; however, BufferedAmount does not reset to zero once the channel
|
||||||
|
// closes.
|
||||||
|
BufferedAmount uint64
|
||||||
|
|
||||||
|
// BufferedAmountLowThreshold represents the threshold at which the
|
||||||
|
// bufferedAmount is considered to be low. When the bufferedAmount decreases
|
||||||
|
// from above this threshold to equal or below it, the bufferedamountlow
|
||||||
|
// event fires. BufferedAmountLowThreshold is initially zero on each new
|
||||||
|
// RTCDataChannel, but the application may change its value at any time.
|
||||||
|
BufferedAmountLowThreshold uint64
|
||||||
|
|
||||||
|
// The binaryType represents attribute MUST, on getting, return the value to
|
||||||
|
// which it was last set. On setting, if the new value is either the string
|
||||||
|
// "blob" or the string "arraybuffer", then set the IDL attribute to this
|
||||||
|
// new value. Otherwise, throw a SyntaxError. When an RTCDataChannel object
|
||||||
|
// is created, the binaryType attribute MUST be initialized to the string
|
||||||
|
// "blob". This attribute controls how binary data is exposed to scripts.
|
||||||
|
// binaryType string
|
||||||
|
|
||||||
|
// OnOpen func()
|
||||||
|
// OnBufferedAmountLow func()
|
||||||
|
// OnError func()
|
||||||
|
// OnClose func()
|
||||||
|
OnMessage func(datachannel.Payload)
|
||||||
|
|
||||||
rtcPeerConnection *RTCPeerConnection
|
rtcPeerConnection *RTCPeerConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Close closes the RTCDataChannel. It may be called regardless of whether the
|
||||||
|
// // RTCDataChannel object was created by this peer or the remote peer.
|
||||||
|
// func (d *RTCDataChannel) Close() error {
|
||||||
|
// if d.ReadyState == RTCDataChannelStateClosing || d.ReadyState == RTCDataChannelStateClosed {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// d.ReadyState = RTCDataChannelStateClosing
|
||||||
|
//
|
||||||
|
// // if err := d.rtcPeerConnection.networkManager.SendOpenChannelMessage(d.ID, d.Label); err != nil {
|
||||||
|
// // return &rtcerr.UnknownError{Err: err}
|
||||||
|
// // }
|
||||||
|
// return nil
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
// SendOpenChannelMessage is a test to send OpenChannel manually
|
// SendOpenChannelMessage is a test to send OpenChannel manually
|
||||||
func (d *RTCDataChannel) SendOpenChannelMessage() error {
|
func (d *RTCDataChannel) SendOpenChannelMessage() error {
|
||||||
if err := d.rtcPeerConnection.networkManager.SendOpenChannelMessage(d.ID, d.Label); err != nil {
|
if err := d.rtcPeerConnection.networkManager.SendOpenChannelMessage(*d.ID, d.Label); err != nil {
|
||||||
return &rtcerr.UnknownError{Err: err}
|
return &rtcerr.UnknownError{Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -31,7 +116,7 @@ func (d *RTCDataChannel) SendOpenChannelMessage() error {
|
|||||||
|
|
||||||
// Send sends the passed message to the DataChannel peer
|
// Send sends the passed message to the DataChannel peer
|
||||||
func (d *RTCDataChannel) Send(p datachannel.Payload) error {
|
func (d *RTCDataChannel) Send(p datachannel.Payload) error {
|
||||||
if err := d.rtcPeerConnection.networkManager.SendDataChannelMessage(p, d.ID); err != nil {
|
if err := d.rtcPeerConnection.networkManager.SendDataChannelMessage(p, *d.ID); err != nil {
|
||||||
return &rtcerr.UnknownError{Err: err}
|
return &rtcerr.UnknownError{Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@@ -10,16 +10,16 @@ func TestGenerateDataChannelID(t *testing.T) {
|
|||||||
c *RTCPeerConnection
|
c *RTCPeerConnection
|
||||||
result uint16
|
result uint16
|
||||||
}{
|
}{
|
||||||
{true, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{}}, 0},
|
{true, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{}}, 0},
|
||||||
{true, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil}}, 0},
|
{true, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil}}, 0},
|
||||||
{true, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil}}, 2},
|
{true, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil}}, 2},
|
||||||
{true, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil, 2: nil}}, 4},
|
{true, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil, 2: nil}}, 4},
|
||||||
{true, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil, 4: nil}}, 2},
|
{true, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil, 4: nil}}, 2},
|
||||||
{false, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{}}, 1},
|
{false, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{}}, 1},
|
||||||
{false, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil}}, 1},
|
{false, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{0: nil}}, 1},
|
||||||
{false, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil}}, 3},
|
{false, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil}}, 3},
|
||||||
{false, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil, 3: nil}}, 5},
|
{false, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil, 3: nil}}, 5},
|
||||||
{false, &RTCPeerConnection{sctp: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil, 5: nil}}, 3},
|
{false, &RTCPeerConnection{SctpTransport: newRTCSctpTransport(), dataChannels: map[uint16]*RTCDataChannel{1: nil, 5: nil}}, 3},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
@@ -5,8 +5,7 @@ package webrtc
|
|||||||
type RTCDataChannelInit struct {
|
type RTCDataChannelInit struct {
|
||||||
// Ordered indicates if data is allowed to be delivered out of order. The
|
// Ordered indicates if data is allowed to be delivered out of order. The
|
||||||
// default value of true, guarantees that data will be delivered in order.
|
// default value of true, guarantees that data will be delivered in order.
|
||||||
// TODO make sure defaults to true
|
Ordered *bool
|
||||||
Ordered bool
|
|
||||||
|
|
||||||
// MaxPacketLifeTime limits the time (in milliseconds) during which the
|
// MaxPacketLifeTime limits the time (in milliseconds) during which the
|
||||||
// channel will transmit or retransmit data if not acknowledged. This value
|
// channel will transmit or retransmit data if not acknowledged. This value
|
||||||
@@ -19,7 +18,7 @@ type RTCDataChannelInit struct {
|
|||||||
MaxRetransmits *uint16
|
MaxRetransmits *uint16
|
||||||
|
|
||||||
// Protocol describes the subprotocol name used for this channel.
|
// Protocol describes the subprotocol name used for this channel.
|
||||||
Protocol string
|
Protocol *string
|
||||||
|
|
||||||
// Negotiated describes if the data channel is created by the local peer or
|
// Negotiated describes if the data channel is created by the local peer or
|
||||||
// the remote peer. The default value of false tells the user agent to
|
// the remote peer. The default value of false tells the user agent to
|
||||||
@@ -27,11 +26,11 @@ type RTCDataChannelInit struct {
|
|||||||
// corresponding RTCDataChannel. If set to true, it is up to the application
|
// corresponding RTCDataChannel. If set to true, it is up to the application
|
||||||
// to negotiate the channel and create an RTCDataChannel with the same id
|
// to negotiate the channel and create an RTCDataChannel with the same id
|
||||||
// at the other peer.
|
// at the other peer.
|
||||||
Negotiated bool
|
Negotiated *bool
|
||||||
|
|
||||||
// ID overrides the default selection of ID for this channel.
|
// ID overrides the default selection of ID for this channel.
|
||||||
ID uint16
|
ID *uint16
|
||||||
|
|
||||||
// Priority describes the priority of this channel.
|
// Priority describes the priority of this channel.
|
||||||
Priority RTCPriorityType
|
Priority *RTCPriorityType
|
||||||
}
|
}
|
||||||
|
@@ -67,7 +67,7 @@ type RTCPeerConnection struct {
|
|||||||
|
|
||||||
// IceConnectionState attribute returns the ICE connection state of the
|
// IceConnectionState attribute returns the ICE connection state of the
|
||||||
// RTCPeerConnection instance.
|
// RTCPeerConnection instance.
|
||||||
// IceConnectionState RTCIceConnectionState
|
// IceConnectionState RTCIceConnectionState // FIXME SWAP-FOR-THIS
|
||||||
IceConnectionState ice.ConnectionState // FIXME REMOVE
|
IceConnectionState ice.ConnectionState // FIXME REMOVE
|
||||||
|
|
||||||
// ConnectionState attribute returns the connection state of the
|
// ConnectionState attribute returns the connection state of the
|
||||||
@@ -81,15 +81,15 @@ type RTCPeerConnection struct {
|
|||||||
isClosed bool
|
isClosed bool
|
||||||
negotiationNeeded bool // FIXME NOT-USED
|
negotiationNeeded bool // FIXME NOT-USED
|
||||||
|
|
||||||
// lastOffer string
|
LastOffer string // FIXME NOT-USED
|
||||||
// lastAnswer string
|
LastAnswer string // FIXME NOT-USED
|
||||||
|
|
||||||
// Media
|
// Media
|
||||||
mediaEngine *MediaEngine
|
mediaEngine *MediaEngine
|
||||||
rtpTransceivers []*RTCRtpTransceiver
|
rtpTransceivers []*RTCRtpTransceiver
|
||||||
|
|
||||||
// SCTP
|
// SctpTransport
|
||||||
sctp *RTCSctpTransport
|
SctpTransport *RTCSctpTransport
|
||||||
|
|
||||||
// DataChannels
|
// DataChannels
|
||||||
dataChannels map[uint16]*RTCDataChannel
|
dataChannels map[uint16]*RTCDataChannel
|
||||||
@@ -123,7 +123,7 @@ func New(configuration RTCConfiguration) (*RTCPeerConnection, error) {
|
|||||||
SignalingState: RTCSignalingStateStable,
|
SignalingState: RTCSignalingStateStable,
|
||||||
ConnectionState: RTCPeerConnectionStateNew,
|
ConnectionState: RTCPeerConnectionStateNew,
|
||||||
mediaEngine: DefaultMediaEngine,
|
mediaEngine: DefaultMediaEngine,
|
||||||
sctp: newRTCSctpTransport(),
|
SctpTransport: newRTCSctpTransport(),
|
||||||
dataChannels: make(map[uint16]*RTCDataChannel),
|
dataChannels: make(map[uint16]*RTCDataChannel),
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
@@ -553,31 +553,105 @@ func (pc *RTCPeerConnection) AddTransceiver() RTCRtpTransceiver {
|
|||||||
// --- FIXME - BELOW CODE NEEDS RE-ORGANIZATION - https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api
|
// --- FIXME - BELOW CODE NEEDS RE-ORGANIZATION - https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
// CreateDataChannel creates a new RTCDataChannel object with the given label and optitional options.
|
// CreateDataChannel creates a new RTCDataChannel object with the given label
|
||||||
|
// and optitional RTCDataChannelInit used to configure properties of the
|
||||||
|
// underlying channel such as data reliability.
|
||||||
func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataChannelInit) (*RTCDataChannel, error) {
|
func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataChannelInit) (*RTCDataChannel, error) {
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #2)
|
||||||
if pc.isClosed {
|
if pc.isClosed {
|
||||||
return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
|
return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5)
|
||||||
if len(label) > 65535 {
|
if len(label) > 65535 {
|
||||||
return nil, &rtcerr.TypeError{Err: ErrInvalidValue}
|
return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defaults
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #3)
|
||||||
ordered := true
|
// Some variables defined explicitly despite their implicit zero values to
|
||||||
priority := RTCPriorityTypeLow
|
// allow better readability to understand what is happening. Additionally,
|
||||||
negotiated := false
|
// some members are set to a non zero value default due to the default
|
||||||
|
// definitions in https://w3c.github.io/webrtc-pc/#dom-rtcdatachannelinit
|
||||||
|
// which are later overwriten by the options if any were specified.
|
||||||
|
channel := RTCDataChannel{
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #4)
|
||||||
|
Label: label,
|
||||||
|
Ordered: true,
|
||||||
|
MaxPacketLifeTime: nil,
|
||||||
|
MaxRetransmits: nil,
|
||||||
|
Protocol: "",
|
||||||
|
Negotiated: false,
|
||||||
|
ID: nil,
|
||||||
|
Priority: RTCPriorityTypeLow,
|
||||||
|
// https://w3c.github.io/webrtc-pc/#dfn-create-an-rtcdatachannel (Step #2)
|
||||||
|
ReadyState: RTCDataChannelStateConnecting,
|
||||||
|
// https://w3c.github.io/webrtc-pc/#dfn-create-an-rtcdatachannel (Step #3)
|
||||||
|
BufferedAmount: 0,
|
||||||
|
}
|
||||||
|
|
||||||
if options != nil {
|
if options != nil {
|
||||||
ordered = options.Ordered
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #7)
|
||||||
priority = options.Priority
|
if options.MaxPacketLifeTime != nil {
|
||||||
negotiated = options.Negotiated
|
channel.MaxPacketLifeTime = options.MaxPacketLifeTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #8)
|
||||||
|
if options.MaxRetransmits != nil {
|
||||||
|
channel.MaxRetransmits = options.MaxRetransmits
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #9)
|
||||||
|
if options.Ordered != nil {
|
||||||
|
channel.Ordered = *options.Ordered
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #10)
|
||||||
|
if options.Protocol != nil {
|
||||||
|
channel.Protocol = *options.Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #12)
|
||||||
|
if options.Negotiated != nil {
|
||||||
|
channel.Negotiated = *options.Negotiated
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #13)
|
||||||
|
if options.ID != nil && channel.Negotiated {
|
||||||
|
channel.ID = options.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #15)
|
||||||
|
if options.Priority != nil {
|
||||||
|
channel.Priority = *options.Priority
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #11)
|
||||||
|
if len(channel.Protocol) > 65535 {
|
||||||
|
return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #14)
|
||||||
|
if channel.Negotiated && channel.ID == nil {
|
||||||
|
return nil, &rtcerr.TypeError{Err: ErrNegotiatedWithoutID}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #16)
|
||||||
|
if channel.MaxPacketLifeTime != nil && channel.MaxRetransmits != nil {
|
||||||
|
return nil, &rtcerr.TypeError{Err: ErrRetransmitsOrPacketLifeTime}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createdatachannel (Step #17)
|
||||||
|
|
||||||
|
// FIXME - Where sctp transport is going to go
|
||||||
|
// if pc.SctpTransport == nil {
|
||||||
|
// pc.SctpTransport = &RTCSctpTransport{
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
var id uint16
|
var id uint16
|
||||||
if negotiated {
|
if !channel.Negotiated {
|
||||||
id = options.ID
|
|
||||||
} else {
|
|
||||||
var err error
|
var err error
|
||||||
id, err = pc.generateDataChannelID(true) // TODO: base on DTLS role
|
id, err = pc.generateDataChannelID(true) // TODO: base on DTLS role
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -585,30 +659,46 @@ func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataCha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if id > 65534 {
|
// // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #18)
|
||||||
return nil, &rtcerr.TypeError{Err: ErrInvalidValue}
|
if *channel.ID > 65534 {
|
||||||
|
return nil, &rtcerr.TypeError{Err: ErrMaxDataChannelID}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pc.sctp.State == RTCSctpTransportStateConnected &&
|
if pc.SctpTransport.State == RTCSctpTransportStateConnected &&
|
||||||
id >= pc.sctp.MaxChannels {
|
id >= *pc.SctpTransport.MaxChannels {
|
||||||
return nil, &rtcerr.OperationError{Err: ErrMaxDataChannels}
|
return nil, &rtcerr.OperationError{Err: ErrMaxDataChannelID}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ordered // TODO
|
// _ = ordered // TODO
|
||||||
_ = priority // TODO
|
// _ = priority // TODO
|
||||||
res := &RTCDataChannel{
|
// res := &RTCDataChannel{
|
||||||
Label: label,
|
// Label: label,
|
||||||
ID: id,
|
// ID: id,
|
||||||
rtcPeerConnection: pc,
|
// rtcPeerConnection: pc,
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Remember datachannel
|
// Remember datachannel
|
||||||
pc.dataChannels[id] = res
|
pc.dataChannels[id] = &channel
|
||||||
|
|
||||||
// Send opening message
|
// Send opening message
|
||||||
// pc.networkManager.SendOpenChannelMessage(id, label)
|
// pc.networkManager.SendOpenChannelMessage(id, label)
|
||||||
|
|
||||||
return res, nil
|
return &channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *RTCPeerConnection) generateDataChannelID(client bool) (uint16, error) {
|
||||||
|
var id uint16
|
||||||
|
if !client {
|
||||||
|
id++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; id < *pc.SctpTransport.MaxChannels-1; id += 2 {
|
||||||
|
_, ok := pc.dataChannels[id]
|
||||||
|
if !ok {
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, &rtcerr.OperationError{Err: ErrMaxDataChannelID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
|
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
|
||||||
@@ -649,7 +739,7 @@ func (pc *RTCPeerConnection) Close() error {
|
|||||||
|
|
||||||
/* Everything below is private */
|
/* Everything below is private */
|
||||||
func (pc *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buffers chan<- *rtp.Packet) {
|
func (pc *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buffers chan<- *rtp.Packet) {
|
||||||
if pc.Ontrack == nil {
|
if pc.OnTrack == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,7 +788,8 @@ func (pc *RTCPeerConnection) dataChannelEventHandler(e network.DataChannelEvent)
|
|||||||
|
|
||||||
switch event := e.(type) {
|
switch event := e.(type) {
|
||||||
case *network.DataChannelCreated:
|
case *network.DataChannelCreated:
|
||||||
newDataChannel := &RTCDataChannel{ID: event.StreamIdentifier(), Label: event.Label, rtcPeerConnection: pc}
|
id := event.StreamIdentifier()
|
||||||
|
newDataChannel := &RTCDataChannel{ID: &id, Label: event.Label, rtcPeerConnection: pc}
|
||||||
pc.dataChannels[e.StreamIdentifier()] = newDataChannel
|
pc.dataChannels[e.StreamIdentifier()] = newDataChannel
|
||||||
if pc.OnDataChannel != nil {
|
if pc.OnDataChannel != nil {
|
||||||
go pc.OnDataChannel(newDataChannel)
|
go pc.OnDataChannel(newDataChannel)
|
||||||
@@ -710,10 +801,10 @@ func (pc *RTCPeerConnection) dataChannelEventHandler(e network.DataChannelEvent)
|
|||||||
datachannel.RLock()
|
datachannel.RLock()
|
||||||
defer datachannel.RUnlock()
|
defer datachannel.RUnlock()
|
||||||
|
|
||||||
if datachannel.Onmessage != nil {
|
if datachannel.OnMessage != nil {
|
||||||
go datachannel.Onmessage(event.Payload)
|
go datachannel.OnMessage(event.Payload)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Onmessage has not been set for Datachannel %s %d \n", datachannel.Label, e.StreamIdentifier())
|
fmt.Printf("OnMessage has not been set for Datachannel %s %d \n", datachannel.Label, e.StreamIdentifier())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("No datachannel found for streamIdentifier %d \n", e.StreamIdentifier())
|
fmt.Printf("No datachannel found for streamIdentifier %d \n", e.StreamIdentifier())
|
||||||
@@ -815,21 +906,6 @@ func (pc *RTCPeerConnection) addDataMediaSection(d *sdp.SessionDescription, midV
|
|||||||
d.WithMedia(media)
|
d.WithMedia(media)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *RTCPeerConnection) generateDataChannelID(client bool) (uint16, error) {
|
|
||||||
var id uint16
|
|
||||||
if !client {
|
|
||||||
id++
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; id < pc.sctp.MaxChannels-1; id += 2 {
|
|
||||||
_, ok := pc.dataChannels[id]
|
|
||||||
if !ok {
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, &rtcerr.OperationError{Err: ErrMaxDataChannels}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRTCTrack is used to create a new RTCTrack
|
// NewRTCTrack is used to create a new RTCTrack
|
||||||
func (pc *RTCPeerConnection) NewRTCTrack(payloadType uint8, id, label string) (*RTCTrack, error) {
|
func (pc *RTCPeerConnection) NewRTCTrack(payloadType uint8, id, label string) (*RTCTrack, error) {
|
||||||
codec, err := pc.mediaEngine.getCodec(payloadType)
|
codec, err := pc.mediaEngine.getCodec(payloadType)
|
||||||
|
@@ -251,6 +251,17 @@ func TestRTCPeerConnection_GetConfiguration(t *testing.T) {
|
|||||||
assert.Equal(t, expected.IceCandidatePoolSize, actual.IceCandidatePoolSize)
|
assert.Equal(t, expected.IceCandidatePoolSize, actual.IceCandidatePoolSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - This unittest needs to be completed when CreateDataChannel is complete
|
||||||
|
// func TestRTCPeerConnection_CreateDataChannel(t *testing.T) {
|
||||||
|
// pc, err := New(RTCConfiguration{})
|
||||||
|
// assert.Nil(t, err)
|
||||||
|
//
|
||||||
|
// _, err = pc.CreateDataChannel("data", &RTCDataChannelInit{
|
||||||
|
//
|
||||||
|
// })
|
||||||
|
// assert.Nil(t, err)
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO Fix this test
|
// TODO Fix this test
|
||||||
const minimalOffer = `v=0
|
const minimalOffer = `v=0
|
||||||
o=- 7193157174393298413 2 IN IP4 127.0.0.1
|
o=- 7193157174393298413 2 IN IP4 127.0.0.1
|
||||||
|
@@ -1,14 +1,27 @@
|
|||||||
package webrtc
|
package webrtc
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
// RTCSctpTransport provides details about the SCTP transport.
|
// RTCSctpTransport provides details about the SCTP transport.
|
||||||
type RTCSctpTransport struct {
|
type RTCSctpTransport struct {
|
||||||
State RTCSctpTransportState // TODO: Set RTCSctpTransportState
|
// Transport represents the transport over which all SCTP packets for data
|
||||||
// transport *RTCDtlsTransport // TODO: DTLS introspection API
|
// channels will be sent and received.
|
||||||
|
Transport RTCDtlsTransport
|
||||||
|
|
||||||
|
// State represents the current state of the SCTP transport.
|
||||||
|
State RTCSctpTransportState
|
||||||
|
|
||||||
|
// MaxMessageSize represents the maximum size of data that can be passed to
|
||||||
|
// RTCDataChannel's send() method.
|
||||||
MaxMessageSize float64
|
MaxMessageSize float64
|
||||||
MaxChannels uint16
|
|
||||||
// onstatechange func()
|
// MaxChannels represents the maximum amount of RTCDataChannel's that can
|
||||||
|
// be used simultaneously.
|
||||||
|
MaxChannels *uint16
|
||||||
|
|
||||||
|
// OnStateChange func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRTCSctpTransport() *RTCSctpTransport {
|
func newRTCSctpTransport() *RTCSctpTransport {
|
||||||
@@ -50,5 +63,6 @@ func (r *RTCSctpTransport) calcMessageSize(remoteMaxMessageSize, canSendSize flo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RTCSctpTransport) updateMaxChannels() {
|
func (r *RTCSctpTransport) updateMaxChannels() {
|
||||||
r.MaxChannels = 65535 // TODO: Get from implementation
|
val := uint16(65535)
|
||||||
|
r.MaxChannels = &val // TODO: Get from implementation
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user