Files
webrtc/rtcdatachannel.go

154 lines
3.5 KiB
Go

package webrtc
import (
"sync"
"github.com/pions/webrtc/pkg/datachannel"
)
// RTCDataChannel represents a WebRTC DataChannel
// The RTCDataChannel interface represents a network channel
// which can be used for bidirectional peer-to-peer transfers of arbitrary data
type RTCDataChannel struct {
sync.RWMutex
Onmessage func(datachannel.Payload)
ID uint16
Label string
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"
}
}
// RTCDataChannelInit can be used to configure properties of the underlying channel such as data reliability.
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
}
var id uint16
if negotiated {
id = options.ID
} else {
var err error
id, err = r.generateDataChannelID(true) // TODO: base on DTLS role
if err != nil {
return nil, err
}
}
if id > 65534 {
return nil, &TypeError{Err: ErrInvalidValue}
}
if r.sctp.State == RTCSctpTransportStateConnected &&
id >= r.sctp.MaxChannels {
return nil, &OperationError{Err: ErrMaxDataChannels}
}
_ = ordered // TODO
_ = priority // TODO
res := &RTCDataChannel{
Label: label,
ID: id,
rtcPeerConnection: r,
}
// Remember datachannel
r.dataChannels[id] = res
// Send opening message
// r.networkManager.SendOpenChannelMessage(id, label)
return res, nil
}
func (r *RTCPeerConnection) generateDataChannelID(client bool) (uint16, error) {
var id uint16
if !client {
id++
}
for ; id < r.sctp.MaxChannels-1; id += 2 {
_, ok := r.dataChannels[id]
if !ok {
return id, nil
}
}
return 0, &OperationError{Err: ErrMaxDataChannels}
}
// SendOpenChannelMessage is a test to send OpenChannel manually
func (d *RTCDataChannel) SendOpenChannelMessage() error {
if err := d.rtcPeerConnection.networkManager.SendOpenChannelMessage(d.ID, d.Label); err != nil {
return &UnknownError{Err: err}
}
return nil
}
// Send sends the passed message to the DataChannel peer
func (d *RTCDataChannel) Send(p datachannel.Payload) error {
if err := d.rtcPeerConnection.networkManager.SendDataChannelMessage(p, d.ID); err != nil {
return &UnknownError{Err: err}
}
return nil
}