ComunicRTCProxy/relay.go

137 lines
2.9 KiB
Go

/// RTC Relay
///
/// @author Pierre Hubert
package main
import (
"encoding/json"
"log"
"time"
"github.com/pion/webrtc/v2"
)
const (
// SDP This is a SDP signal
SDP = iota
// CANDIDATE This is a candidate
CANDIDATE = iota
)
type receivedSignal struct {
peerID string
callHash string
sigType uint
offer webrtc.SessionDescription
candidate webrtc.ICECandidateInit
}
/// We keep for each connection its channel
var closeChan = make(chan string)
var connections = make(map[string]chan receivedSignal)
/// Process incoming messages
func onSignal(callHash, peerID string, data map[string]interface{}) {
// Close all the channels that requested so
processCloseRequests()
// Decode received signal
newSignal := receivedSignal{
peerID: peerID,
callHash: callHash,
}
if data["type"] == "SDP" {
newSignal.sigType = SDP
newSignal.offer.Type = webrtc.SDPTypeOffer
newSignal.offer.SDP = data["data"].(map[string]interface{})["sdp"].(string)
} else if data["type"] == "CANDIDATE" {
newSignal.sigType = CANDIDATE
// I have to re-encode data to initialize ICECandidate
var enc []byte
enc, err := json.Marshal(data["data"])
if err != nil {
log.Printf("Could not re-encode candidate ! %s", err)
return
}
err = json.Unmarshal(enc, &newSignal.candidate)
if err != nil {
log.Printf("Discarding invalid candidate: %s", err)
return
}
} else {
log.Fatalf("Invalid signal type: %s !", data["type"])
}
// Check if we are attempting to connect as viewer to a non existing channel
if _, ok := connections[callHash]; !ok {
if peerID != "0" || newSignal.sigType != SDP {
println("Attempting to connect as viewer | send candidate to a non-ready broadcast!")
return
}
}
// Handle new offers
if newSignal.sigType == SDP && peerID == "0" {
// Check if we are overwriting another connection
if _, ok := connections[callHash]; ok {
closeConnection(callHash)
}
connections[callHash] = make(chan receivedSignal, 10)
go newCall(newSignal, connections[callHash])
} else {
// Forward the message to the channel
connections[callHash] <- newSignal
}
}
/// Close a connection
func closeConnection(callHash string) {
log.Printf("Closing call %s", callHash)
if val, ok := connections[callHash]; ok {
close(val)
delete(connections, callHash)
}
}
// Ask for a channel to be closed
func askForClose(callHash string) {
closeChan <- callHash
}
// Process channel close requests (in thread safe way)
func processCloseRequests() {
for {
select {
case id := <-closeChan:
closeConnection(id)
case <-time.After(time.Millisecond * 10):
return
}
}
}
/// Start new call
func newCall(mainOffer receivedSignal, ch chan receivedSignal) {
// Since we are answering use PayloadTypes declared by offerer
mediaEngine := webrtc.MediaEngine{}
err := mediaEngine.PopulateFromSDP(mainOffer.offer)
if err != nil {
log.Println("Error: invalid data in offer!", err)
askForClose(mainOffer.callHash)
return
}
}