450 lines
15 KiB
Go
450 lines
15 KiB
Go
package localserver
|
|
|
|
// import (
|
|
// context "context"
|
|
// "encoding/json"
|
|
// "fmt"
|
|
// "io"
|
|
// sync "sync"
|
|
|
|
// "github.com/pion/webrtc/v3"
|
|
// )
|
|
|
|
// type WebrtcFsManager struct {
|
|
// stream SignalingService_LinkClient
|
|
// DatachannelManager DataChannelManager
|
|
// LocalSD map[string]*webrtc.SessionDescription
|
|
// RTCPeerConnections map[string]*webrtc.PeerConnection
|
|
// DataChannels map[string]*webrtc.DataChannel
|
|
// PendingCandidates map[string][]*webrtc.ICECandidate
|
|
// CandidateChannel chan *IncomingCandidate
|
|
// CandidateMux *sync.RWMutex
|
|
// RTCPeerConnectionMapMux *sync.RWMutex
|
|
// DataChannelMapMux *sync.RWMutex
|
|
// LocalSDMapMux *sync.RWMutex
|
|
// }
|
|
|
|
// func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager *WebrtcFsManager, err error) {
|
|
// webrtcFsManager = &WebrtcFsManager{
|
|
// DatachannelManager: dataChannelManager,
|
|
// DataChannels: make(map[string]*webrtc.DataChannel),
|
|
// PendingCandidates: make(map[string][]*webrtc.ICECandidate),
|
|
// LocalSD: make(map[string]*webrtc.SessionDescription),
|
|
// RTCPeerConnections: make(map[string]*webrtc.PeerConnection),
|
|
// RTCPeerConnectionMapMux: &sync.RWMutex{},
|
|
// LocalSDMapMux: &sync.RWMutex{},
|
|
// CandidateMux: &sync.RWMutex{},
|
|
// DataChannelMapMux: &sync.RWMutex{},
|
|
// }
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
|
|
// bs, err := json.Marshal(payload)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// err = wf.stream.Send(&SignalingMessage{
|
|
// Type: messageType,
|
|
// From: from,
|
|
// To: to,
|
|
// Payload: bs,
|
|
// })
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
|
|
// peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// rawOffer, err := peerConnection.CreateOffer(nil)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// if err = peerConnection.SetLocalDescription(rawOffer); err != nil {
|
|
// return
|
|
// }
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// logger.Println("adding for target", target)
|
|
// wf.RTCPeerConnections[target] = peerConnection
|
|
// wf.RTCPeerConnectionMapMux.Unlock()
|
|
// err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{
|
|
// "to": target,
|
|
// "from": "lolo_local_serv",
|
|
// "sdp": rawOffer.SDP,
|
|
// })
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
|
|
// done, errCh := make(chan struct{}), make(chan error)
|
|
// go func() {
|
|
// peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
|
|
// if err != nil {
|
|
// errCh <- err
|
|
// return
|
|
// }
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// wf.RTCPeerConnections[req[FROM]] = peerConnection
|
|
// wf.RTCPeerConnectionMapMux.Unlock()
|
|
// offer := webrtc.SessionDescription{
|
|
// Type: webrtc.SDPTypeOffer,
|
|
// SDP: req[SDP],
|
|
// }
|
|
// if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
|
// errCh <- err
|
|
// return
|
|
// }
|
|
// rawAnswer, err := peerConnection.CreateAnswer(nil)
|
|
// if err != nil {
|
|
// errCh <- err
|
|
// return
|
|
// }
|
|
// wf.LocalSDMapMux.Lock()
|
|
// wf.LocalSD[req[FROM]] = &rawAnswer
|
|
// wf.LocalSDMapMux.Unlock()
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_ANSWER_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "none",
|
|
// // Payload: map[string]string{
|
|
// // "to": req[FROM],
|
|
// // "from": "lolo_local_serv",
|
|
// // "sdp": rawAnswer.SDP,
|
|
// // },
|
|
// // }); err != nil {
|
|
// // errCh <- err
|
|
// // return
|
|
// // }
|
|
// done <- struct{}{}
|
|
// }()
|
|
// select {
|
|
// case <-done:
|
|
// return
|
|
// case err = <-errCh:
|
|
// return
|
|
// case <-ctx.Done():
|
|
// err = ctx.Err()
|
|
// return
|
|
// }
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) {
|
|
// wf.RTCPeerConnectionMapMux.RLock()
|
|
// defer wf.RTCPeerConnectionMapMux.RUnlock()
|
|
// defer func() {
|
|
// if r := recover(); err != nil {
|
|
// logger.Printf("recover from panic : %v\n", r)
|
|
// }
|
|
// }()
|
|
// if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
|
// err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM])
|
|
// return
|
|
// }
|
|
// peerConnnection := wf.RTCPeerConnections[req[FROM]]
|
|
// if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{
|
|
// Type: webrtc.SDPTypeAnswer,
|
|
// SDP: req[SDP],
|
|
// }); err != nil {
|
|
// return
|
|
// }
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_COUNTER_OFFER_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "none",
|
|
// // Payload: map[string]string{
|
|
// // "from": "lolo_local_serv",
|
|
// // "to": req[FROM],
|
|
// // },
|
|
// // }); err != nil {
|
|
// // return
|
|
// // }
|
|
// wf.CandidateMux.RLock()
|
|
// for range wf.PendingCandidates[req[FROM]] {
|
|
// logger.Println("sending candidate to", req[FROM])
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_CANDIDATE_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "none",
|
|
// // Payload: map[string]string{
|
|
// // "from": "lolo_local_serv",
|
|
// // "to": req[FROM],
|
|
// // "candidate": candidate.ToJSON().Candidate,
|
|
// // "sdpMid": *candidate.ToJSON().SDPMid,
|
|
// // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
|
// // },
|
|
// // }); err != nil {
|
|
// // return
|
|
// // }
|
|
// }
|
|
// wf.CandidateMux.RUnlock()
|
|
// wf.CandidateMux.Lock()
|
|
// delete(wf.PendingCandidates, req[FROM])
|
|
// wf.CandidateMux.Unlock()
|
|
// wf.LocalSDMapMux.Lock()
|
|
// delete(wf.LocalSD, req[FROM])
|
|
// wf.LocalSDMapMux.Unlock()
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
|
|
// wf.RTCPeerConnectionMapMux.RLock()
|
|
// if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
|
// err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
|
|
// return
|
|
// }
|
|
// connection := wf.RTCPeerConnections[req[FROM]]
|
|
// wf.RTCPeerConnectionMapMux.RUnlock()
|
|
// wf.LocalSDMapMux.RLock()
|
|
// if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
|
|
// return
|
|
// }
|
|
// wf.LocalSDMapMux.RUnlock()
|
|
// wf.CandidateMux.RLock()
|
|
// for range wf.PendingCandidates[req[FROM]] {
|
|
// // logger.Println("sending candidate to", req[FROM])
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_CANDIDATE_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "none",
|
|
// // Payload: map[string]string{
|
|
// // "from": "lolo_local_serv",
|
|
// // "to": req[FROM],
|
|
// // "candidate": candidate.ToJSON().Candidate,
|
|
// // "sdpMid": *candidate.ToJSON().SDPMid,
|
|
// // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
|
// // },
|
|
// // }); err != nil {
|
|
// // return
|
|
// // }
|
|
// }
|
|
// wf.CandidateMux.RUnlock()
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
|
|
// defer func() {
|
|
// if r := recover(); err != nil {
|
|
// logger.Printf("recover from panic : %v\n", r)
|
|
// }
|
|
// }()
|
|
// s := webrtc.SettingEngine{}
|
|
// s.DetachDataChannels()
|
|
|
|
// api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
|
|
|
// config := webrtc.Configuration{
|
|
// ICEServers: []webrtc.ICEServer{
|
|
// {
|
|
// URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302?transport=tcp"},
|
|
// },
|
|
// },
|
|
// SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
|
|
// }
|
|
// peerConnection, err = api.NewPeerConnection(config)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// if peerType == webrtc.SDPTypeOffer {
|
|
// maxRetransmits := uint16(100)
|
|
// channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
|
|
// MaxRetransmits: &maxRetransmits,
|
|
// })
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// channel.OnOpen(func() {
|
|
// logger.Println("channel opened")
|
|
// if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
|
|
// logger.Println(chanErr)
|
|
// }
|
|
// })
|
|
// channel.OnMessage(func(msg webrtc.DataChannelMessage) {
|
|
// logger.Printf("new message %s\n", string(msg.Data))
|
|
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
|
// From: target,
|
|
// Type: "test",
|
|
// Payload: &DatachannelMessagePayload{},
|
|
// }, channel)
|
|
// select {
|
|
// case <-done:
|
|
// //logger.Println("done with success")
|
|
// case e := <-errCh:
|
|
// logger.Println(e)
|
|
// logger.Println("this is impossible")
|
|
// }
|
|
// })
|
|
// wf.DataChannelMapMux.Lock()
|
|
// wf.DataChannels[target] = channel
|
|
// wf.DataChannelMapMux.Unlock()
|
|
// }
|
|
// peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
|
|
// logger.Printf("ICE connection state has changed %s\n", is.String())
|
|
// if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
|
|
// logger.Println(is)
|
|
// }
|
|
// })
|
|
// peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
|
|
// dc.OnOpen(func() {
|
|
// logger.Printf("got a new open datachannel %s\n", dc.Label())
|
|
// dataChann, err := dc.Detach()
|
|
// if err != nil {
|
|
// logger.Println(err)
|
|
// return
|
|
// }
|
|
// for {
|
|
// var x []byte = make([]byte, 2<<15)
|
|
// n, _, err := dataChann.ReadDataChannel(x)
|
|
// if err != nil {
|
|
// logger.Println(err)
|
|
// if err == io.EOF {
|
|
// return
|
|
// }
|
|
// continue
|
|
// }
|
|
// go func(msg []byte) {
|
|
// var dataChannelMessage DatachannelMessage
|
|
// if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
|
|
// logger.Println(unmarshalErr)
|
|
// return
|
|
// }
|
|
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
|
// From: dataChannelMessage.From,
|
|
// Type: dataChannelMessage.Type,
|
|
// Payload: dataChannelMessage.Payload,
|
|
// }, dc)
|
|
// select {
|
|
// case <-done:
|
|
// //logger.Println("done with success")
|
|
// case e := <-errCh:
|
|
// logger.Println(e)
|
|
// logger.Println("this is impossible")
|
|
// }
|
|
// }(x[:n])
|
|
// }
|
|
// })
|
|
// // dc.OnMessage(func(msg webrtc.DataChannelMessage) {
|
|
// // var dataChannelMessage DatachannelMessage
|
|
// // if unmarshalErr := json.Unmarshal(msg.Data, &dataChannelMessage); unmarshalErr != nil {
|
|
// // logger.Println(unmarshalErr)
|
|
// // return
|
|
// // }
|
|
// // done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
|
// // From: dataChannelMessage.From,
|
|
// // Type: dataChannelMessage.Type,
|
|
// // Payload: dataChannelMessage.Payload,
|
|
// // }, dc)
|
|
// // select {
|
|
// // case <-done:
|
|
// // //logger.Println("done with success")
|
|
// // case e := <-errCh:
|
|
// // logger.Println(e)
|
|
// // logger.Println("this is impossible")
|
|
// // }
|
|
// // })
|
|
// })
|
|
// peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
|
// if i == nil {
|
|
// return
|
|
// }
|
|
// wf.CandidateMux.Lock()
|
|
// defer wf.CandidateMux.Unlock()
|
|
// desc := peerConnection.RemoteDescription()
|
|
// if desc == nil {
|
|
// wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
|
|
// } else {
|
|
// logger.Println(i)
|
|
// if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
|
|
// logger.Println(iceCandidateErr)
|
|
// }
|
|
// }
|
|
// })
|
|
// peerConnection.OnNegotiationNeeded(func() {
|
|
// if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
|
// for _, connection := range wf.RTCPeerConnections {
|
|
// if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
|
// localSd, err := connection.CreateOffer(nil)
|
|
// if err != nil {
|
|
// logger.Println(err)
|
|
// return
|
|
// }
|
|
// if err = connection.SetLocalDescription(localSd); err != nil {
|
|
// logger.Println(err)
|
|
// return
|
|
// }
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "",
|
|
// // Payload: map[string]string{
|
|
// // "to": id,
|
|
// // "sdp": localSd.SDP,
|
|
// // },
|
|
// // }); err != nil {
|
|
// // logger.Println(err)
|
|
// // return
|
|
// // }
|
|
// }
|
|
// }
|
|
// }
|
|
// })
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sdp string) (err error) {
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
|
// if _, ok := wf.RTCPeerConnections[from]; !ok {
|
|
// err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
|
// return
|
|
// }
|
|
// if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
|
|
// err = fmt.Errorf("rennego called in wrong state")
|
|
// return
|
|
// }
|
|
// if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
|
|
// return
|
|
// }
|
|
// localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
|
|
// return
|
|
// }
|
|
// // if err = wf.stream.Send(&Request{
|
|
// // Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
|
|
// // From: "lolo_local_serv",
|
|
// // Token: "",
|
|
// // Payload: map[string]string{
|
|
// // "to": from,
|
|
// // "sdp": localSd.SDP,
|
|
// // },
|
|
// // }); err != nil {
|
|
// // logger.Println(err)
|
|
// // return
|
|
// // }
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp string) (err error) {
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
|
// if _, ok := wf.RTCPeerConnections[from]; !ok {
|
|
// err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
|
// return
|
|
// }
|
|
// err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
|
|
// return
|
|
// }
|
|
|
|
// func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
|
|
// wf.RTCPeerConnectionMapMux.Lock()
|
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
|
// err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
|
|
// return
|
|
// }
|