diff --git a/src/bandwidthRtc.ts b/src/bandwidthRtc.ts index 9df6e56..95d3ba8 100644 --- a/src/bandwidthRtc.ts +++ b/src/bandwidthRtc.ts @@ -6,6 +6,7 @@ import { jwtDecode } from "jwt-decode"; import { AudioLevelChangeHandler, BandwidthRtcError, + DtmfSentHandler, EndpointType, HangupResult, OutboundConnectionResult, @@ -23,6 +24,7 @@ class BandwidthRtc { private streamAvailableHandler?: { (event: RtcStream): void }; private streamUnavailableHandler?: { (event: RtcStream): void }; private readyHandler?: { (readyMetadata: ReadyMetadata): void }; + private dtmfSentHandler?: DtmfSentHandler; private logLevel?: LogLevel; private delegate?: BandwidthRtcV1; @@ -81,6 +83,10 @@ class BandwidthRtc { this.delegate!.onReady(this.readyHandler); } + if (this.dtmfSentHandler) { + this.delegate!.onDtmfSent(this.dtmfSentHandler); + } + return this.delegate!.connect(authParams, options); } @@ -127,6 +133,18 @@ class BandwidthRtc { } } + /** + * Set the function that will be called each time a DTMF tone is actually played + * on a published stream. + * @param callback callback function + */ + onDtmfSent(callback: DtmfSentHandler): void { + this.dtmfSentHandler = callback; + if (this.delegate) { + this.delegate.onDtmfSent(callback); + } + } + /** * Returns an array of available video input devices */ diff --git a/src/types.ts b/src/types.ts index 4eacdf5..98185b4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,15 @@ export enum EndpointType { export type AudioLevelChangeHandler = { (audioLevel: AudioLevel): void }; +export interface DtmfSentEvent { + /** The DTMF tone that was just played, e.g. "1" or "#" */ + tone: string; + /** Id of the published MediaStream the tone was sent on */ + streamId: string; +} + +export type DtmfSentHandler = { (event: DtmfSentEvent): void }; + /** * @property {string} endpointToken - The endpoint token is a "string" in the JWT format. * To be possible the token utilization, parse the token using "jwt_decode" function, the expected result should be a {@link JwtPayload} object. diff --git a/src/v1/bandwidthRtc.ts b/src/v1/bandwidthRtc.ts index 5b441f2..e5eb2ba 100644 --- a/src/v1/bandwidthRtc.ts +++ b/src/v1/bandwidthRtc.ts @@ -8,6 +8,7 @@ import logger, { LogLevel } from "../logging"; import { AudioLevelChangeHandler, BandwidthRtcError, + DtmfSentHandler, EndpointType, HangupResult, MediaType, @@ -91,6 +92,7 @@ export class BandwidthRtc { private streamAvailableHandler?: { (event: RtcStream): void }; private streamUnavailableHandler?: { (event: RtcStream): void }; private readyHandler?: { (readyMetadata: ReadyMetadata): void }; + private dtmfSentHandler?: DtmfSentHandler; // Caller identity for pending subscribe tracks, keyed by track id. The gateway // negotiates a fresh subscribe track per call and rides the call's metadata on @@ -167,6 +169,15 @@ export class BandwidthRtc { this.readyHandler = callback; } + /** + * Set the function that will be called each time a DTMF tone is actually played + * on a published stream (fires once per tone via the native RTCDTMFSender "tonechange" event). + * @param callback callback function + */ + onDtmfSent(callback: DtmfSentHandler): void { + this.dtmfSentHandler = callback; + } + /** * Publish media to the Bandwidth WebRTC platform * @@ -741,6 +752,13 @@ export class BandwidthRtc { const dtmfSender = transceiver.sender.dtmf; if (track.kind === TRACK_KIND_AUDIO && dtmfSender && !this.localDtmfSenders.has(mediaStream.id)) { this.localDtmfSenders.set(mediaStream.id, dtmfSender); + // "tonechange" fires once per tone as it's actually played, and once more with + // an empty tone when the queue drains; only the former is a tone being "sent". + dtmfSender.addEventListener?.("tonechange", (event: RTCDTMFToneChangeEvent) => { + if (event.tone) { + this.dtmfSentHandler?.({ tone: event.tone, streamId: mediaStream.id }); + } + }); } if (track.kind === TRACK_KIND_AUDIO) {