399 lines
12 KiB
Go
399 lines
12 KiB
Go
package localserver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
type SquadVideoChannelsHandler struct {
|
|
SquadId string
|
|
SquadName string
|
|
HostId string
|
|
SquadMembersId []string
|
|
DataChannels map[string]*DataChannel
|
|
VideoChanDataChannels map[string]*DataChannel
|
|
VideoChanDataChannelsFlag *uint32
|
|
DataChannelsFlag *uint32
|
|
VideoChannelsFlag *uint32
|
|
VideoChannel *VideoChannel
|
|
reqChans []chan<- *SquadRequest
|
|
}
|
|
|
|
func NewSquadVideoChannelsHandler(hostId string, squadId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (squadVideoChannelsHandler *SquadVideoChannelsHandler, err error) {
|
|
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
// logger.Printf("creating videoChannels directory for squad %s...\n", squadId)
|
|
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"), 0700)
|
|
if mkdirErr != nil {
|
|
return nil, mkdirErr
|
|
}
|
|
file, ferr := os.Create(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "videoChannelConfig.json"))
|
|
defer func() {
|
|
_ = file.Close()
|
|
}()
|
|
if ferr != nil {
|
|
return nil, ferr
|
|
}
|
|
baseConfig := VideoChannelConfig{
|
|
ID: MEETING,
|
|
Owner: owner,
|
|
ChannelType: "public",
|
|
CurrentMembersId: make([]string, 0),
|
|
Members: make([]string, 0),
|
|
}
|
|
bs, jsonErr := json.Marshal(baseConfig)
|
|
if jsonErr != nil {
|
|
return nil, jsonErr
|
|
}
|
|
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
|
|
return nil, writeErr
|
|
}
|
|
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
|
|
var bs []byte
|
|
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "videoChannelConfig.json"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// logger.Println(string(bs))
|
|
var vcc VideoChannelConfig
|
|
if err = json.Unmarshal(bs, &vcc); err != nil {
|
|
return nil, err
|
|
}
|
|
// logger.Println("videoChannels data :", vcc.ID, vcc.ChannelType, vcc.Owner, vcc.Members)
|
|
vc := NewVideoChannel(vcc.ID, vcc.Owner, vcc.ChannelType, vcc.Members, make([]string, 0), make(map[string]*VideoChannelMember))
|
|
videoChannelFlag := uint32(0)
|
|
videoChanDCFlag := uint32(0)
|
|
squadVideoChannelsHandler = &SquadVideoChannelsHandler{
|
|
SquadId: squadId,
|
|
HostId: hostId,
|
|
SquadMembersId: authorizedMembers,
|
|
DataChannels: dataChannels,
|
|
VideoChanDataChannels: make(map[string]*DataChannel),
|
|
VideoChanDataChannelsFlag: &videoChanDCFlag,
|
|
DataChannelsFlag: dataChannelFlag,
|
|
VideoChannel: vc,
|
|
VideoChannelsFlag: &videoChannelFlag,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) {
|
|
done, errCh := make(chan struct{}), make(chan error)
|
|
go func() {
|
|
if err := atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
|
|
if _, ok := zvch.VideoChanDataChannels[to]; ok {
|
|
bs, jsonErr := json.Marshal(&SquadResponse{
|
|
Type: reqType,
|
|
From: from,
|
|
To: to,
|
|
Payload: payload,
|
|
})
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
err = zvch.VideoChanDataChannels[to].DataChannel.SendText(string(bs))
|
|
}
|
|
return
|
|
}); err != nil {
|
|
errCh <- err
|
|
return
|
|
}
|
|
done <- struct{}{}
|
|
}()
|
|
return done, errCh
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error) {
|
|
reqChan, done, errCh = make(chan *SquadRequest), make(chan struct{}), make(chan error)
|
|
zvch.reqChans = append(zvch.reqChans, reqChan)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
done <- struct{}{}
|
|
return
|
|
case req := <-publisher:
|
|
if err := zvch.handleSquadRequest(ctx, req); err != nil {
|
|
errCh <- err
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
|
|
d, e := zvch.sendDataChannelMessage(string(VIDEO_CHANNEL_WEBRTC_CANDIDATE), from, to, map[string]interface{}{
|
|
"from": from,
|
|
"to": to,
|
|
"candidate": candidate.ToJSON().Candidate,
|
|
"sdpMid": *candidate.ToJSON().SDPMid,
|
|
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
|
})
|
|
select {
|
|
case <-d:
|
|
case err = <-e:
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) JoinVideoChannel(channelId string, userId string, sdp string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
videoChannel := zvch.VideoChannel
|
|
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId, userId)
|
|
videoChannel.CurrentMembers[userId] = &VideoChannelMember{}
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zvch.sendDataChannelMessage(USER_JOINED_VIDEO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case <-e:
|
|
// logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
signalMembers(zvch.SquadMembersId)
|
|
d, e := videoChannel.HandleOffer(context.Background(), channelId, userId, sdp, zvch.HostId, zvch.sendDataChannelMessage, zvch.signalCandidate)
|
|
select {
|
|
case <-d:
|
|
case err = <-e:
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) LeaveVideoChannel(channelId string, userId string) (err error) {
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
videoChannel := zvch.VideoChannel
|
|
var index int
|
|
var contain bool
|
|
for i, v := range videoChannel.CurrentMembersId {
|
|
if v == userId {
|
|
index = i
|
|
contain = true
|
|
}
|
|
}
|
|
if !contain {
|
|
err = fmt.Errorf("this channel does not contain the provided user Id")
|
|
return
|
|
}
|
|
defer videoChannel.HandleLeavingMember(userId)
|
|
if len(videoChannel.CurrentMembersId) <= 1 {
|
|
videoChannel.CurrentMembersId = make([]string, 0)
|
|
} else {
|
|
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId[:index], videoChannel.CurrentMembersId[index+1:]...)
|
|
}
|
|
delete(videoChannel.CurrentMembers, userId)
|
|
signalMembers := func(members []string) {
|
|
for _, u := range members {
|
|
done, e := zvch.sendDataChannelMessage(USER_LEFT_VIDEO_CHANNEL, "node", u, map[string]interface{}{
|
|
"userId": userId,
|
|
"channelId": channelId,
|
|
})
|
|
select {
|
|
case <-done:
|
|
case <-e:
|
|
// logger.Println(err)
|
|
}
|
|
}
|
|
}
|
|
signalMembers(zvch.SquadMembersId)
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
|
|
var label string
|
|
_ = atomicallyExecute(dc.l, func() (err error) {
|
|
label = dc.DataChannel.Label()
|
|
return
|
|
})
|
|
if strings.Contains(label, "video_channel_data") {
|
|
command := strings.Split(label, "|")
|
|
catched = true
|
|
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
|
|
zvch.VideoChanDataChannels[command[1]] = dc
|
|
return
|
|
})
|
|
dc.DataChannel.OnOpen(func() {
|
|
fmt.Println("datachann in squad video chann fking created")
|
|
bs, err := json.Marshal(map[string]any{
|
|
"type": "init_video_channel",
|
|
"from": NodeID,
|
|
"to": command[1],
|
|
"payload": map[string]any{
|
|
"videoChannel": zvch.VideoChannel,
|
|
},
|
|
})
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
_ = dc.DataChannel.SendText(string(bs))
|
|
})
|
|
dc.DataChannel.OnClose(func() {
|
|
fmt.Println("closing gratefully video channel dc...")
|
|
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
|
|
delete(zvch.VideoChanDataChannels, command[1])
|
|
fmt.Println("dc closed gracefully...")
|
|
return
|
|
})
|
|
})
|
|
dc.DataChannel.OnError(func(err error) {
|
|
fmt.Println("error in video channel dc...")
|
|
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
|
|
delete(zvch.VideoChanDataChannels, command[1])
|
|
fmt.Println("dc closed on error...")
|
|
return
|
|
})
|
|
})
|
|
}
|
|
return
|
|
}
|
|
|
|
func (zvch *SquadVideoChannelsHandler) handleSquadRequest(ctx context.Context, req *SquadRequest) (err error) {
|
|
switch req.ReqType {
|
|
case LEAVE_ZONE:
|
|
// logger.Println("*-----------------handling leaving squad---------------*")
|
|
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
vc := zvch.VideoChannel
|
|
var contain bool
|
|
var id string
|
|
for _, member := range vc.CurrentMembersId {
|
|
if member == req.Payload["userId"].(string) {
|
|
id = member
|
|
contain = true
|
|
// logger.Printf("*------------------id is %s--------------*\n", id)
|
|
break
|
|
}
|
|
}
|
|
if contain {
|
|
err = zvch.LeaveVideoChannel(vc.ID, id)
|
|
break
|
|
}
|
|
|
|
case string(REMOVED_SQUAD_AUTHORIZED_MEMBER):
|
|
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
var index int
|
|
var found bool
|
|
for i, m := range zvch.SquadMembersId {
|
|
if m == req.Payload["userId"].(string) {
|
|
index = i
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
err = fmt.Errorf("no such member in squad")
|
|
return
|
|
}
|
|
zvch.SquadMembersId = append(zvch.SquadMembersId[:index], zvch.SquadMembersId[index+1:]...)
|
|
case string(NEW_AUTHORIZED_SQUAD_MEMBER):
|
|
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
|
|
return
|
|
}
|
|
var contain bool
|
|
for _, m := range zvch.SquadMembersId {
|
|
if m == req.Payload["userId"].(string) {
|
|
contain = true
|
|
break
|
|
}
|
|
}
|
|
if !contain {
|
|
zvch.SquadMembersId = append(zvch.SquadMembersId, req.Payload["userId"].(string))
|
|
}
|
|
case JOIN_VIDEO_CHANNEL:
|
|
fmt.Println("wuwuuwuwwuuwuw i got this")
|
|
fmt.Println(req.Payload)
|
|
if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.JoinVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
case LEAVE_VIDEO_CHANNEL:
|
|
if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = zvch.LeaveVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string))
|
|
case string(VIDEO_CHANNEL_WEBRTC_COUNTER_OFFER):
|
|
// logger.Println("handling video channel counter offer")
|
|
if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
err = zvch.VideoChannel.HandleCounterOffer(ctx, req.Payload["userId"].(string), zvch.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER):
|
|
if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
err = zvch.VideoChannel.HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zvch.sendDataChannelMessage)
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER):
|
|
if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
|
|
return
|
|
}
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
err = zvch.VideoChannel.HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
|
|
return
|
|
})
|
|
case string(VIDEO_CHANNEL_WEBRTC_CANDIDATE):
|
|
// logger.Println("handling video channel webrtc candidate")
|
|
// logger.Println(req.Payload)
|
|
if err = VerifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil {
|
|
return
|
|
}
|
|
// logger.Println(req.Payload)
|
|
i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
|
|
if convErr != nil {
|
|
return convErr
|
|
}
|
|
SDPMLineIndex := uint16(i)
|
|
sdpMid := req.Payload["sdpMid"].(string)
|
|
// logger.Println(sdpMid, SDPMLineIndex)
|
|
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
|
|
fmt.Println("uwuwuw adding candidate", req.Payload["candidate"].(string))
|
|
err = zvch.VideoChannel.AddCandidate(&webrtc.ICECandidateInit{
|
|
Candidate: req.Payload["candidate"].(string),
|
|
SDPMid: &sdpMid,
|
|
SDPMLineIndex: &SDPMLineIndex,
|
|
}, req.Payload["userId"].(string))
|
|
return
|
|
})
|
|
default:
|
|
// logger.Println("video channel handler still in process of implementation")
|
|
}
|
|
return
|
|
}
|