From 4419e315039f306a462b61b44c2b317188be4d0e Mon Sep 17 00:00:00 2001 From: gari Date: Sun, 13 Apr 2025 11:21:09 +0200 Subject: [PATCH] fix: update WebRTC SDP handling and logging for offer/answer exchange --- internal/api/preview.go | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/internal/api/preview.go b/internal/api/preview.go index b2bdc20..e004c76 100644 --- a/internal/api/preview.go +++ b/internal/api/preview.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/go-gst/go-glib/glib" "github.com/go-gst/go-gst/gst" + "github.com/go-gst/go-gst/gst/gstsdp" + "github.com/go-gst/go-gst/gst/gstwebrtc" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" @@ -94,7 +96,7 @@ func (api *API) previewHandler(c echo.Context) error { // on-negotiation-needed: Triggered when webrtcbin needs to create an offer or answer. // In our case (server receiving an offer), this fires after the remote description is set. previewPipeline.webrtcbin.Connect("on-negotiation-needed", func(self *gst.Element) { - log.Info().Msg("🧠 Negotiation needed, creating answer...") + log.Info().Msg("Negotiation needed, creating answer") // Create Answer promise, err := self.Emit("create-answer") @@ -108,26 +110,26 @@ func (api *API) previewHandler(c echo.Context) error { } // Handle the promise result (asynchronously) - promise.(*gst.Promise).Interrupt() // Interrupt any previous waits - promise.(*gst.Promise).Wait() // Wait for the answer to be created + promise.(*gst.Promise).Interrupt() // Interrupt any previous waits + promise.(*gst.Promise).Await(c.Request().Context()) // Wait for the answer to be created reply := promise.(*gst.Promise).GetReply() if reply == nil { log.Error().Msg("Promise reply for create-answer was nil") return } - answerValue, ok := reply.GetValue("answer") - if !ok || answerValue == nil { + answerValue, err := reply.GetValue("answer") + if err != nil || answerValue == nil { log.Error().Msg("Failed to get answer from promise reply") return } - answer, ok := answerValue.(*gst.WebRTCSessionDescription) + answer, ok := answerValue.(*gstwebrtc.SessionDescription) if !ok || answer == nil { log.Error().Msg("Answer value is not a WebRTCSessionDescription") return } - log.Debug().Str("sdp", answer.GetSDP().String()).Msg("✅ Answer created") + log.Debug().Str("sdp", answer.SDP().String()).Msg("Answer created") // Set Local Description promise, err = self.Emit("set-local-description", answer) @@ -141,13 +143,13 @@ func (api *API) previewHandler(c echo.Context) error { } promise.(*gst.Promise).Interrupt() // Interrupt any previous waits - log.Info().Msg("➡️ Set local description (answer)") + log.Info().Msg("Set local description (answer)") // Send Answer back to the client - log.Info().Msg("📨 Sending SDP answer back to browser") + log.Info().Msg("Sending SDP answer back to browser") response := SignalMessage{ Type: "answer", - SDP: answer.GetSDP().String(), + SDP: answer.SDP().String(), } msg, err := json.Marshal(response) if err != nil { @@ -185,7 +187,7 @@ func (api *API) previewHandler(c echo.Context) error { // --- WebSocket Message Loop --- for { - messageType, message, err := conn.ReadMessage() + _, message, err := conn.ReadMessage() if err != nil { log.Error().Err(err).Msg("WebSocket read error") break @@ -199,20 +201,21 @@ func (api *API) previewHandler(c echo.Context) error { switch signal.Type { case "offer": - log.Info().Msg("📥 Received SDP offer from browser") - log.Debug().Str("sdp", signal.SDP).Msg("Offer SDP") + log.Info().Msg("Received SDP offer from browser") + //log.Debug().Str("sdp", signal.SDP).Msg("Offer SDP") // Create WebRTCSessionDescription for the offer - offerDesc, err := gst.NewWebRTCSessionDescription(gst.WebRTCSDPTypeOffer, gst.NewSDPMessageFromString(signal.SDP)) + offerMsg, err := gstsdp.ParseSDPMessage(signal.SDP) if err != nil { - log.Error().Err(err).Msg("Failed to create offer description from SDP") + log.Error().Err(err).Msg("Failed to parse SDP message") continue } + offerDesc := gstwebrtc.NewSessionDescription(gstwebrtc.SDP_TYPE_OFFER, offerMsg) + promise := gst.NewPromise() // Set Remote Description // This will trigger on-negotiation-needed if successful and state allows - promise, err := previewPipeline.webrtcbin.Emit("set-remote-description", offerDesc) - if err != nil { + if _, err := previewPipeline.webrtcbin.Emit("set-remote-description", offerDesc, promise); err != nil { log.Error().Err(err).Msg("Failed to emit set-remote-description") continue } @@ -220,9 +223,10 @@ func (api *API) previewHandler(c echo.Context) error { log.Error().Msg("Emit set-remote-description returned nil promise") continue } - promise.(*gst.Promise).Interrupt() // Interrupt previous waits if any + //promise.Interrupt() // Interrupt previous waits if any + promise.Await(c.Request().Context()) // Wait for the remote description to be set - log.Info().Msg("➡️ Set remote description (offer)") + log.Info().Msg("Set remote description (offer)") // Answer creation is now handled in on-negotiation-needed case "ice": @@ -230,7 +234,7 @@ func (api *API) previewHandler(c echo.Context) error { log.Warn().Msg("Received ICE signal with nil ICE field") continue } - log.Debug().Str("candidate", signal.ICE.Candidate).Uint16("mlineindex", signal.ICE.SDPMLineIndex).Str("sdpMid", signal.ICE.SDPMid).Msg("➕ Received ICE candidate from browser") + log.Debug().Str("candidate", signal.ICE.Candidate).Uint16("mlineindex", signal.ICE.SDPMLineIndex).Str("sdpMid", signal.ICE.SDPMid).Msg("Received ICE candidate from browser") // Add ICE Candidate // Note: The signal takes mlineindex (uint) and candidate (string).