package localserver import ( "context" "fmt" "strconv" "github.com/pion/webrtc/v3" ) type GrpcRequestType string const ( PEER_CONNECTION_REQUEST GrpcRequestType = "peer_connection_request" ) const ( OFFER ReqType = "offer" ANSWER ReqType = "answer" COUNTER_OFFER ReqType = "webrtc_counter_offer" JOIN_HOSTED_SQUAD ReqType = "join_hosted_squad" HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_access_denied" HOSTED_SQUAD_STOP_CALL ReqType = "hosted_squad_stop_call" HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted" LEAVE_HOSTED_SQUAD ReqType = "leave_hosted_squad" INCOMING_MEMBER_HOSTED ReqType = "incoming_member_hosted" LEAVING_MEMBER_HOSTED ReqType = "leaving_member_hosted" HOSTED_SQUAD_WEBRTC_OFFER ReqType = "hosted_squad_offer" HOSTED_SQUAD_WEBRTC_ANSWER ReqType = "hosted_squad_answer" HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer" HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer" HOSTED_SQUAD_WEBRTC_COUNTER_OFFER ReqType = "hosted_squad_webrtc_counter_offer" HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate" HOSTED_SQUAD_REMOVE_VIDEO ReqType = "hosted_squad_remove_video" GET_HOSTED_SQUAD_TRACKS ReqType = "hosted_squad_get_tracks" NEW_HOSTED_SQUAD = "new_hosted_squad" ) type WebRTCGrpcMiddleware struct { Manager *WebRTCCallManager stream GrpcManager_LinkClient } func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) { webrtcGrpcMiddleware = &WebRTCGrpcMiddleware{ Manager: manager, } return } func validateRequest(req map[string]string, entries ...string) (err error) { for _, entry := range entries { if _, ok := req[entry]; !ok { err = fmt.Errorf("no field %s in req payload", entry) return } } return } func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { err = wgm.stream.Send(&Request{ Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE), From: wgm.Manager.ID, Token: "none", Payload: map[string]string{ "from": wgm.Manager.ID, "to": to, "candidate": candidate.ToJSON().Candidate, "sdpMid": *candidate.ToJSON().SDPMid, "sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }, }) return } func (wgm *WebRTCGrpcMiddleware) Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) { done, errCh := make(chan struct{}), make(chan error) go func() { defer func() { done <- struct{}{} }() switch req.Type { case NEW_HOSTED_SQUAD: if err := validateRequest(req.Payload, "ID"); err != nil { errCh <- err return } logger.Println("new squad incoming") wgm.Manager.SquadMapMux.Lock() wgm.Manager.Squads[req.Payload["ID"]] = &Squad{ ID: req.Payload["ID"], Members: []string{}, } wgm.Manager.SquadMapMux.Unlock() case string(HOSTED_SQUAD_STOP_CALL): logger.Println("quit squad called") if err := validateRequest(req.Payload, FROM, "squadId"); err != nil { errCh <- err return } wgm.Manager.HandleLeavingMember(req.Payload[FROM], req.Payload["squadId"]) done <- struct{}{} case string(PEER_CONNECTION_REQUEST): if err := validateRequest(req.Payload, FROM, TO); err != nil { errCh <- err return } logger.Println("creating offer for peer") if err := wgm.Manager.CreateOffer(ctx, req.Payload[FROM], req.Payload[TO], wgm.signalCandidate); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_OFFER): if err := validateRequest(req.GetPayload(), FROM, TO, SDP, SQUAD_ID); err != nil { errCh <- err return } if err := wgm.Manager.HandleOffer(ctx, req.GetPayload(), wgm.signalCandidate); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_ANSWER): if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { errCh <- err return } if err := wgm.Manager.HandleAnswer(ctx, req.GetPayload()); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER): if err := validateRequest(req.GetPayload(), FROM); err != nil { errCh <- err return } if err := wgm.Manager.HandleCounterOffer(ctx, req.Payload); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER): logger.Println("received negotiation answer") if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { errCh <- err return } if err := wgm.Manager.HandleRennegotiationAnswer(req.Payload[FROM], req.Payload[SDP]); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER): logger.Println("received negotiation offer") if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { errCh <- err return } if err := wgm.Manager.HandleRennegotiationOffer(req.Payload[FROM], req.Payload[SDP]); err != nil { errCh <- err return } case string(HOSTED_SQUAD_WEBRTC_CANDIDATE): if err := validateRequest(req.GetPayload(), FROM, "candidate", "sdpMlineIndex", "sdpMid"); err != nil { errCh <- err return } logger.Println(req.Payload) i, err := strconv.Atoi(req.Payload["sdpMlineIndex"]) if err != nil { errCh <- err return } sdpMlineIndex := uint16(i) sdpMid := req.Payload["sdpMid"] logger.Println(sdpMid, sdpMlineIndex) if err := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{ Candidate: req.Payload["candidate"], SDPMid: &sdpMid, SDPMLineIndex: &sdpMlineIndex, }, req.Payload[FROM]); err != nil { errCh <- err return } default: } }() select { case <-ctx.Done(): err = ctx.Err() return case <-done: return case err = <-errCh: return } }