mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-06-05 03:49:44 +00:00
218 lines
5.2 KiB
Go
218 lines
5.2 KiB
Go
// Package wbstream implements the WB Stream WebRTC provider.
|
|
package wbstream
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
lksdk "github.com/livekit/server-sdk-go/v2"
|
|
"github.com/pion/webrtc/v4"
|
|
)
|
|
|
|
const (
|
|
wsURL = "wss://wbstream01-el.wb.ru:7880"
|
|
)
|
|
|
|
var (
|
|
// ErrPeerClosed is returned when an operation is attempted on a closed peer.
|
|
ErrPeerClosed = errors.New("peer closed")
|
|
// ErrSendQueueFull is returned when the transmission queue is full.
|
|
ErrSendQueueFull = errors.New("send queue full")
|
|
// ErrLiveKitNotConnected is returned when the LiveKit room is not connected.
|
|
ErrLiveKitNotConnected = errors.New("livekit room not connected")
|
|
// ErrVideoNotSupported is returned when video tracks are not supported by this provider.
|
|
ErrVideoNotSupported = errors.New("video tracks not supported yet in wbstream")
|
|
)
|
|
|
|
// Peer represents a WB Stream WebRTC connection using LiveKit.
|
|
type Peer struct {
|
|
roomURL string
|
|
name string
|
|
room *lksdk.Room
|
|
onData func([]byte)
|
|
onReconnect func(*webrtc.DataChannel)
|
|
shouldReconnect func() bool
|
|
onEnded func(string)
|
|
sendQueue chan []byte
|
|
closed atomic.Bool
|
|
done chan struct{}
|
|
cancel context.CancelFunc
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// NewPeer creates a new WB Stream provider peer.
|
|
func NewPeer(ctx context.Context, roomURL, name string, onData func([]byte)) (*Peer, error) {
|
|
_, cancel := context.WithCancel(ctx)
|
|
return &Peer{
|
|
roomURL: roomURL,
|
|
name: name,
|
|
onData: onData,
|
|
sendQueue: make(chan []byte, 5000),
|
|
done: make(chan struct{}),
|
|
cancel: cancel,
|
|
}, nil
|
|
}
|
|
|
|
// Connect starts the WebRTC connection process.
|
|
func (p *Peer) Connect(ctx context.Context) error {
|
|
token, err := p.getRoomToken(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("get room token: %w", err)
|
|
}
|
|
|
|
roomCB := &lksdk.RoomCallback{
|
|
ParticipantCallback: lksdk.ParticipantCallback{
|
|
OnDataReceived: func(data []byte, _ lksdk.DataReceiveParams) {
|
|
if p.onData != nil {
|
|
p.onData(data)
|
|
}
|
|
},
|
|
},
|
|
OnDisconnected: func() {
|
|
if p.onEnded != nil {
|
|
p.onEnded("disconnected from livekit")
|
|
}
|
|
},
|
|
}
|
|
|
|
room, err := lksdk.ConnectToRoomWithToken(wsURL, token, roomCB, lksdk.WithAutoSubscribe(true))
|
|
if err != nil {
|
|
return fmt.Errorf("connect to room: %w", err)
|
|
}
|
|
|
|
p.room = room
|
|
p.wg.Add(1)
|
|
go p.processSendQueue()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Peer) getRoomToken(ctx context.Context) (string, error) {
|
|
accessToken, err := registerGuest(ctx, p.name)
|
|
if err != nil {
|
|
return "", fmt.Errorf("register guest: %w", err)
|
|
}
|
|
|
|
roomID := p.roomURL
|
|
if roomID == "" || roomID == "any" {
|
|
roomID, err = createRoom(ctx, accessToken)
|
|
if err != nil {
|
|
return "", fmt.Errorf("create room: %w", err)
|
|
}
|
|
log.Printf("WB Stream room created: %s", roomID)
|
|
log.Printf("To connect client use: -id %s", roomID)
|
|
}
|
|
|
|
if err := joinRoom(ctx, accessToken, roomID); err != nil {
|
|
return "", fmt.Errorf("join room: %w", err)
|
|
}
|
|
|
|
token, err := getToken(ctx, accessToken, roomID, p.name)
|
|
if err != nil {
|
|
return "", fmt.Errorf("get token: %w", err)
|
|
}
|
|
|
|
return token, nil
|
|
}
|
|
|
|
func (p *Peer) processSendQueue() {
|
|
defer p.wg.Done()
|
|
for {
|
|
select {
|
|
case <-p.done:
|
|
return
|
|
case data, ok := <-p.sendQueue:
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := p.room.LocalParticipant.PublishDataPacket(
|
|
lksdk.UserData(data),
|
|
lksdk.WithDataPublishTopic("olcrtc"),
|
|
lksdk.WithDataPublishReliable(true),
|
|
); err != nil {
|
|
log.Printf("WB Stream publish data error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send transmits data to the room.
|
|
func (p *Peer) Send(data []byte) error {
|
|
if p.closed.Load() {
|
|
return ErrPeerClosed
|
|
}
|
|
select {
|
|
case p.sendQueue <- data:
|
|
return nil
|
|
default:
|
|
return ErrSendQueueFull
|
|
}
|
|
}
|
|
|
|
// Close terminates the provider connection.
|
|
func (p *Peer) Close() error {
|
|
if p.closed.CompareAndSwap(false, true) {
|
|
p.cancel()
|
|
close(p.done)
|
|
if p.room != nil {
|
|
p.room.Disconnect()
|
|
}
|
|
close(p.sendQueue)
|
|
p.wg.Wait()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetReconnectCallback is a stub for WB Stream.
|
|
func (p *Peer) SetReconnectCallback(cb func(*webrtc.DataChannel)) {
|
|
p.onReconnect = cb
|
|
}
|
|
|
|
// SetShouldReconnect is a stub for WB Stream.
|
|
func (p *Peer) SetShouldReconnect(fn func() bool) {
|
|
p.shouldReconnect = fn
|
|
}
|
|
|
|
// SetEndedCallback sets the function to call when the session ends.
|
|
func (p *Peer) SetEndedCallback(cb func(string)) {
|
|
p.onEnded = cb
|
|
}
|
|
|
|
// WatchConnection is a stub for WB Stream.
|
|
func (p *Peer) WatchConnection(_ context.Context) {}
|
|
|
|
// CanSend checks if the provider is ready to transmit data.
|
|
func (p *Peer) CanSend() bool {
|
|
return !p.closed.Load() && p.room != nil
|
|
}
|
|
|
|
// GetSendQueue returns the data transmission queue.
|
|
func (p *Peer) GetSendQueue() chan []byte {
|
|
return p.sendQueue
|
|
}
|
|
|
|
// GetBufferedAmount is a stub for WB Stream.
|
|
func (p *Peer) GetBufferedAmount() uint64 {
|
|
return 0
|
|
}
|
|
|
|
// AddVideoTrack adds a video track to the LiveKit room.
|
|
func (p *Peer) AddVideoTrack(track *webrtc.TrackLocalStaticRTP) (*webrtc.RTPSender, error) {
|
|
if p.room == nil || p.room.LocalParticipant == nil {
|
|
return nil, ErrLiveKitNotConnected
|
|
}
|
|
|
|
_, err := p.room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{
|
|
Name: "video",
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to publish track: %w", err)
|
|
}
|
|
|
|
return nil, ErrVideoNotSupported
|
|
}
|