diff --git a/errors.go b/errors.go index 50c2760d..6dee59ed 100644 --- a/errors.go +++ b/errors.go @@ -91,5 +91,33 @@ func (e *SyntaxError) Error() string { return fmt.Sprintf("syntax error: %v", e.Err) } +// Types of TypeError +var ( + ErrInvalidValue = errors.New("invalid value") +) + +// TypeError indicates an issue with a supplied value +type TypeError struct { + Err error +} + +func (e *SyntaxError) Error() string { + return fmt.Sprintf("type error: %v", e.Err) +} + +// Types of OperationError +var ( + ErrMaxDataChannels = errors.New("maximum number of datachannels reached") +) + +// OperationError indicates an issue with execution +type OperationError struct { + Err error +} + +func (e *SyntaxError) Error() string { + return fmt.Sprintf("operation error: %v", e.Err) +} + // ErrUnknownType indicates a Unknown info var ErrUnknownType = errors.New("Unknown") diff --git a/rtcdatachannel.go b/rtcdatachannel.go index fffa4784..42e09d15 100644 --- a/rtcdatachannel.go +++ b/rtcdatachannel.go @@ -19,6 +19,95 @@ type RTCDataChannel struct { rtcPeerConnection *RTCPeerConnection } +// RTCPriorityType determines the priority of a data channel. +type RTCPriorityType int + +const ( + // RTCPriorityTypeVeryLow corresponds to "below normal" + RTCPriorityTypeVeryLow RTCPriorityType = iota + 1 + + // RTCPriorityTypeLow corresponds to "normal" + RTCPriorityTypeLow + + // RTCPriorityTypeMedium corresponds to "high" + RTCPriorityTypeMedium + + // RTCPriorityTypeHigh corresponds to "extra high" + RTCPriorityTypeHigh +) + +func (p RTCPriorityType) String() string { + switch p { + case RTCPriorityTypeVeryLow: + return "very-low" + case RTCPriorityTypeLow: + return "low" + case RTCPriorityTypeMedium: + return "medium" + case RTCPriorityTypeHigh: + return "high" + default: + return "Unknown" + } +} + +type RTCDataChannelInit struct { + Ordered bool + MaxPacketLifeTime *uint16 + MaxRetransmits *uint16 + Protocol string + Negotiated bool + Id uint16 + Priority RTCPriorityType +} + +// CreateDataChannel creates a new RTCDataChannel object with the given label and optitional options. +func (r *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataChannelInit) (*RTCDataChannel, error) { + if r.IsClosed { + return nil, &InvalidStateError{Err: ErrConnectionClosed} + } + + if len(label) > 65535 { + return nil, &TypeError{Err: ErrInvalidValue} + } + + // Defaults + ordered := true + priority := RTCPriorityTypeLow + negotiated := false + + if options != nil { + ordered := options.Ordered + priority := options.Priority + negotiated := options.Negotiated + } + + id := 0 + if negotiated { + id := options.Id + } else { + // TODO: generate id + } + + if id > 65534 { + return nil, &TypeError{Err: ErrInvalidValue} + } + + if r.sctp.State == RTCSctpTransportStateConnected && + id >= r.sctp.MaxChannels { + return nil, &OperationError{Err: ErrMaxDataChannels} + } + + // TODO: Actually allocate datachannel + res := &RTCDataChannel{ + Label: label, + ID: id, + rtcPeerConnection: r, + } + + return res, nil +} + // Send sends the passed message to the DataChannel peer func (r *RTCDataChannel) Send(p datachannel.Payload) error { if err := r.rtcPeerConnection.networkManager.SendDataChannelMessage(p, r.ID); err != nil { diff --git a/rtcpeerconnection.go b/rtcpeerconnection.go index e470aacc..4169afa2 100644 --- a/rtcpeerconnection.go +++ b/rtcpeerconnection.go @@ -94,6 +94,9 @@ type RTCPeerConnection struct { rtpTransceivers []*RTCRtpTransceiver Ontrack func(*RTCTrack) + // SCTP + sctp *RTCSctpTransport + // DataChannels dataChannels map[uint16]*RTCDataChannel Ondatachannel func(*RTCDataChannel) @@ -108,6 +111,7 @@ func New(config RTCConfiguration) (*RTCPeerConnection, error) { signalingState: RTCSignalingStateStable, connectionState: RTCPeerConnectionStateNew, mediaEngine: DefaultMediaEngine, + sctp: newRTCSctpTransport(), dataChannels: make(map[uint16]*RTCDataChannel), } var err error diff --git a/rtcsctptransport.go b/rtcsctptransport.go new file mode 100644 index 00000000..28f2df84 --- /dev/null +++ b/rtcsctptransport.go @@ -0,0 +1,81 @@ +package webrtc + +import "math" + +// RTCSctpTransportState indicates the state of the SCTP transport. +type RTCSctpTransportState int + +const ( + // RTCSctpTransportStateConnecting indicates the RTCSctpTransport is in the process of negotiating an association. + RTCSctpTransportStateConnecting RTCSctpTransportState = iota + 1 + + // RTCSctpTransportStateConnected indicates the negotiation of an association is completed. + RTCSctpTransportStateConnected + + // RTCSctpTransportStateClosed indicates a SHUTDOWN or ABORT chunk is received or when the SCTP association has been closed intentionally. + RTCSctpTransportStateClosed +) + +func (s RTCSctpTransportState) String() string { + switch s { + case RTCSctpTransportStateConnecting: + return "connecting" + case RTCSctpTransportStateConnected: + return "connected" + case RTCSctpTransportStateClosed: + return "closed" + default: + return "Unknown" + } +} + +// RTCSctpTransport provides details about the SCTP transport. +type RTCSctpTransport struct { + State RTCSctpTransportState // TODO: Set RTCSctpTransportState + // transport *RTCDtlsTransport // TODO: DTLS introspection API + MaxMessageSize float64 + MaxChannels uint16 + // onstatechange func() +} + +func newRTCSctpTransport() *RTCSctpTransport { + res := &RTCSctpTransport{ + State: RTCSctpTransportStateConnecting, + } + + res.updateMessageSize() + res.updateMaxChannels() + + return res +} + +func (r *RTCSctpTransport) updateMessageSize() { + var remoteMaxMessageSize float64 = 65536 // TODO: get from SDP + var canSendSize float64 = 65536 // TODO: Get from SCTP implementation + + r.MaxMessageSize = r.calcMessageSize(remoteMaxMessageSize, canSendSize) +} + +func (r *RTCSctpTransport) calcMessageSize(remoteMaxMessageSize, canSendSize float64) float64 { + switch { + case remoteMaxMessageSize == 0 && + canSendSize == 0: + return math.Inf(1) + + case remoteMaxMessageSize == 0: + return canSendSize + + case canSendSize == 0: + return remoteMaxMessageSize + + case canSendSize > remoteMaxMessageSize: + return remoteMaxMessageSize + + default: + return canSendSize + } +} + +func (r *RTCSctpTransport) updateMaxChannels() { + r.maxChannels = 65536 // TODO: Get from implementation +}