Files
client/firebase_answer.go
2025-07-13 22:30:33 +05:30

131 lines
3.1 KiB
Go

package client
import (
"context"
"errors"
"fmt"
"time"
"cloud.google.com/go/firestore"
"firebase.google.com/go"
"google.golang.org/api/option"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/pion/webrtc/v4"
)
type AnswerSignal struct {
peerConnection *PeerConnection
app *firebase.App
client *firestore.Client
docRef *firestore.DocumentRef
ctx context.Context
}
func CreateFirebaseAnswerSignal(ctx context.Context, peerConnection *PeerConnection) *AnswerSignal {
var (
configuration option.ClientOption
app *firebase.App
client *firestore.Client
err error
)
if configuration, err = GetFirebaseConfiguration(); err != nil {
panic(err)
}
if app, err = firebase.NewApp(ctx, nil, configuration); err != nil {
panic(err)
}
if client, err = app.Firestore(ctx); err != nil {
panic(err)
}
return &AnswerSignal{
app: app,
client: client,
peerConnection: peerConnection,
ctx: ctx,
}
}
func (signal *AnswerSignal) Connect(category, connectionLabel string) error {
signal.docRef = signal.client.Collection(category).Doc(connectionLabel)
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
var data map[string]interface{}
loop:
for {
select {
case <-ticker.C:
snapshot, err := signal.docRef.Get(signal.ctx)
if err != nil || snapshot == nil {
if status.Code(err) == codes.NotFound {
continue loop
}
}
data = snapshot.Data()
if currentStatus, exists := data[FieldStatus]; !exists || currentStatus != FieldStatusPending {
continue loop
}
break loop
}
}
fmt.Println("Found Offer. Creating answer...")
return signal.answer(data[FieldOffer].(map[string]interface{}))
}
func (signal *AnswerSignal) answer(offer map[string]interface{}) error {
sdp, ok := offer[FieldSDP].(string)
if !ok {
return fmt.Errorf("invalid SDP format in offer")
}
if err := signal.peerConnection.peerConnection.SetRemoteDescription(webrtc.SessionDescription{
Type: webrtc.SDPTypeOffer,
SDP: sdp,
}); err != nil {
return err
}
answer, err := signal.peerConnection.peerConnection.CreateAnswer(nil)
if err != nil {
return fmt.Errorf("error while creating answer: %w", err)
}
if err := signal.peerConnection.peerConnection.SetLocalDescription(answer); err != nil {
return fmt.Errorf("error while setting local sdp: %w", err)
}
timer := time.NewTicker(30 * time.Second)
defer timer.Stop()
select {
case <-webrtc.GatheringCompletePromise(signal.peerConnection.peerConnection):
fmt.Println("ICE Gathering complete")
case <-timer.C:
return errors.New("failed to gather ICE candidates within 30 seconds")
}
if _, err = signal.docRef.Set(signal.ctx, map[string]interface{}{
FieldAnswer: map[string]interface{}{
FieldSDP: signal.peerConnection.peerConnection.LocalDescription().SDP,
FieldUpdatedAt: firestore.ServerTimestamp,
},
FieldStatus: FieldStatusConnected,
}, firestore.MergeAll); err != nil {
return err
}
return nil
}
func (signal *AnswerSignal) Close() error {
return signal.client.Close()
}