From 472e1f0cea9629a8ab94d5ee63255736c3f1f0e3 Mon Sep 17 00:00:00 2001 From: smoghe-bw Date: Mon, 15 Jun 2026 16:16:05 -0400 Subject: [PATCH] fix: route gateway streamAvailable notification to new onInboundStreamNotification callback Previously the signaling-layer streamAvailable WS event was routed through onStreamAvailable, which fires before the WebRTC ontrack event arrives and therefore has no mediaStream. This broke existing consumers who assume onStreamAvailable always carries a populated mediaStream. Introduce onInboundStreamNotification as the dedicated callback for the pre-media gateway notification (carries callId/autoAccepted, no mediaStream). onStreamAvailable continues to fire only from the WebRTC ontrack handler, so mediaStream is always present and existing consumers are unaffected. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/bandwidthRtc.ts | 18 ++++++++++++++++++ src/v1/bandwidthRtc.ts | 20 ++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/bandwidthRtc.ts b/src/bandwidthRtc.ts index 4cec567..acce6b4 100644 --- a/src/bandwidthRtc.ts +++ b/src/bandwidthRtc.ts @@ -22,6 +22,7 @@ class BandwidthRtc { // Event handlers private streamAvailableHandler?: { (event: RtcStream): void }; private streamUnavailableHandler?: { (event: RtcStream): void }; + private inboundStreamNotificationHandler?: { (event: RtcStream): void }; private readyHandler?: { (readyMetadata: ReadyMetadata): void }; private logLevel?: LogLevel; @@ -77,6 +78,10 @@ class BandwidthRtc { this.delegate!.onStreamUnavailable(this.streamUnavailableHandler); } + if (this.inboundStreamNotificationHandler) { + this.delegate!.onInboundStreamNotification(this.inboundStreamNotificationHandler); + } + if (this.readyHandler) { this.delegate!.onReady(this.readyHandler); } @@ -116,6 +121,19 @@ class BandwidthRtc { } } + /** + * Set the function that will be called when the gateway signals that an inbound + * stream is ready to be accepted or declined, before the WebRTC media arrives. + * Use this to drive accept/decline UI; mediaStream will be undefined at this point. + * @param callback callback function + */ + onInboundStreamNotification(callback: { (event: RtcStream): void }): void { + this.inboundStreamNotificationHandler = callback; + if (this.delegate) { + this.delegate.onInboundStreamNotification(callback); + } + } + /** * Set the function that will be called when a subscribed stream becomes unavailable * @param callback callback function diff --git a/src/v1/bandwidthRtc.ts b/src/v1/bandwidthRtc.ts index 1c5bced..6d3fca9 100644 --- a/src/v1/bandwidthRtc.ts +++ b/src/v1/bandwidthRtc.ts @@ -77,6 +77,7 @@ export class BandwidthRtc { // Event handlers private streamAvailableHandler?: { (event: RtcStream): void }; private streamUnavailableHandler?: { (event: RtcStream): void }; + private inboundStreamNotificationHandler?: { (event: RtcStream): void }; private readyHandler?: { (readyMetadata: ReadyMetadata): void }; /** @@ -109,9 +110,9 @@ export class BandwidthRtc { this.signaling.on("ready", this.handleReady.bind(this)); this.signaling.on("sdpOffer", this.handleSubscribeSdpOffer.bind(this)); this.signaling.on("init", this.init.bind(this)); - this.signaling.on("streamAvailable", ({ callId }: { callId: string }) => { - if (this.streamAvailableHandler) { - this.streamAvailableHandler({ mediaTypes: [MediaType.AUDIO], callId }); + this.signaling.on("streamAvailable", ({ callId, autoAccepted }: { callId: string; autoAccepted: boolean }) => { + if (this.inboundStreamNotificationHandler) { + this.inboundStreamNotificationHandler({ mediaTypes: [MediaType.AUDIO], callId, autoAccepted }); } }); this.signaling.on("streamUnavailable", ({ callId }: { callId: string }) => { @@ -134,13 +135,24 @@ export class BandwidthRtc { } /** - * Set the function that will be called when a subscribed stream becomes available + * Set the function that will be called when a subscribed stream becomes available. + * The RtcStream passed to the callback always contains a populated mediaStream. * @param callback callback function */ onStreamAvailable(callback: { (event: RtcStream): void }): void { this.streamAvailableHandler = callback; } + /** + * Set the function that will be called when the gateway signals that an inbound + * stream is ready to be accepted or declined, before the WebRTC media arrives. + * Use this to drive accept/decline UI; mediaStream will be undefined at this point. + * @param callback callback function + */ + onInboundStreamNotification(callback: { (event: RtcStream): void }): void { + this.inboundStreamNotificationHandler = callback; + } + /** * Set the function that will be called when a subscribed stream becomes unavailable * @param callback callback function