Skip to content

Commit 4980117

Browse files
author
smoghe-bw
committed
test: add DTMF sender and codec preference tests; clean up comments
1 parent 48a68af commit 4980117

2 files changed

Lines changed: 88 additions & 28 deletions

File tree

src/v1/bandwidthRtc.test.ts

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ describe("bandwidthRtcV1 sendDtmf", () => {
7575
brtc.sendDtmf("5");
7676

7777
expect(sender1.insertDTMF).toHaveBeenCalledTimes(1);
78-
expect(sender1.insertDTMF).toHaveBeenCalledWith("5", undefined, undefined);
78+
expect(sender1.insertDTMF).toHaveBeenCalledWith("5", 100, 70);
7979
expect(sender2.insertDTMF).toHaveBeenCalledTimes(1);
80-
expect(sender2.insertDTMF).toHaveBeenCalledWith("5", undefined, undefined);
80+
expect(sender2.insertDTMF).toHaveBeenCalledWith("5", 100, 70);
8181
});
8282

8383
test("calls insertDTMF only on the specified stream when streamId given", () => {
@@ -119,6 +119,80 @@ describe("bandwidthRtcV1 sendDtmf", () => {
119119
});
120120
});
121121

122+
describe("bandwidthRtcV1 addStreamToPublishingPeerConnection", () => {
123+
function makeTransceiver(dtmf: RTCDTMFSender | null = { insertDTMF: jest.fn() } as any) {
124+
return { sender: { dtmf }, setCodecPreferences: jest.fn() };
125+
}
126+
127+
function makeMockStream(id: string, trackKind: string) {
128+
return { id, getTracks: () => [{ kind: trackKind, id: "track-1" }] };
129+
}
130+
131+
function withPublishingPeerConnection(brtc: BandwidthRtc, transceiver: ReturnType<typeof makeTransceiver>) {
132+
(brtc as any).publishingPeerConnection = { addTransceiver: jest.fn().mockReturnValue(transceiver) };
133+
}
134+
135+
test("stores dtmf sender for audio track", () => {
136+
const brtc = new BandwidthRtc();
137+
const dtmfSender = { insertDTMF: jest.fn() };
138+
const transceiver = makeTransceiver(dtmfSender as any);
139+
withPublishingPeerConnection(brtc, transceiver);
140+
141+
(brtc as any).addStreamToPublishingPeerConnection(makeMockStream("stream-1", "audio"));
142+
143+
expect((brtc as any).localDtmfSenders.get("stream-1")).toBe(dtmfSender);
144+
});
145+
146+
test("does not store dtmf sender when sender.dtmf is null", () => {
147+
const brtc = new BandwidthRtc();
148+
withPublishingPeerConnection(brtc, makeTransceiver(null));
149+
150+
(brtc as any).addStreamToPublishingPeerConnection(makeMockStream("stream-1", "audio"));
151+
152+
expect((brtc as any).localDtmfSenders.has("stream-1")).toBe(false);
153+
});
154+
155+
test("appends telephone-event codec when missing from audio preferences", () => {
156+
const brtc = new BandwidthRtc();
157+
const transceiver = makeTransceiver();
158+
withPublishingPeerConnection(brtc, transceiver);
159+
160+
const telephoneEventCodec = { mimeType: "audio/telephone-event", clockRate: 8000 };
161+
(global as any).RTCRtpSender = { getCapabilities: jest.fn().mockReturnValue({ codecs: [telephoneEventCodec] }) };
162+
163+
const opusCodec = { mimeType: "audio/opus", clockRate: 48000 };
164+
(brtc as any).addStreamToPublishingPeerConnection(makeMockStream("stream-1", "audio"), { audio: [opusCodec] });
165+
166+
expect(transceiver.setCodecPreferences).toHaveBeenCalledWith([opusCodec, telephoneEventCodec]);
167+
});
168+
169+
test("does not duplicate telephone-event when already in preferences", () => {
170+
const brtc = new BandwidthRtc();
171+
const transceiver = makeTransceiver();
172+
withPublishingPeerConnection(brtc, transceiver);
173+
174+
const opusCodec = { mimeType: "audio/opus", clockRate: 48000 };
175+
const telephoneEventCodec = { mimeType: "audio/telephone-event", clockRate: 8000 };
176+
(brtc as any).addStreamToPublishingPeerConnection(makeMockStream("stream-1", "audio"), { audio: [opusCodec, telephoneEventCodec] });
177+
178+
expect(transceiver.setCodecPreferences).toHaveBeenCalledWith([opusCodec, telephoneEventCodec]);
179+
expect(transceiver.setCodecPreferences).toHaveBeenCalledTimes(1);
180+
});
181+
182+
test("falls back to original preferences when telephone-event not found in capabilities", () => {
183+
const brtc = new BandwidthRtc();
184+
const transceiver = makeTransceiver();
185+
withPublishingPeerConnection(brtc, transceiver);
186+
187+
(global as any).RTCRtpSender = { getCapabilities: jest.fn().mockReturnValue({ codecs: [] }) };
188+
189+
const opusCodec = { mimeType: "audio/opus", clockRate: 48000 };
190+
(brtc as any).addStreamToPublishingPeerConnection(makeMockStream("stream-1", "audio"), { audio: [opusCodec] });
191+
192+
expect(transceiver.setCodecPreferences).toHaveBeenCalledWith([opusCodec]);
193+
});
194+
});
195+
122196
describe("bandwidthRtcV1 connect method", () => {
123197
beforeAll(() => {
124198
setupNavigatorMocks();

src/v1/bandwidthRtc.ts

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,13 @@ const CONNECTION_STATE_DISCONNECTED = "disconnected";
5757
export class BandwidthRtc {
5858
private options?: RtcOptions;
5959

60-
// Batches diagnostic data for debugging
6160
private diagnosticsBatcher: DiagnosticsBatcher;
62-
63-
// Communicates with the Bandwidth WebRTC platform
6461
private signaling: Signaling;
6562

66-
// One peer for all published (outgoing) streams, one for all subscribed (incoming) streams
63+
// One peer connection for all published (outgoing) streams, one for all subscribed (incoming) streams
6764
private publishingPeerConnection?: RTCPeerConnection;
6865
private subscribingPeerConnection?: RTCPeerConnection;
6966

70-
// Standard datachannels used for platform diagnostics and health checks
7167
private publishHeartbeatDataChannel?: RTCDataChannel;
7268
private publishDiagnosticsDataChannel?: RTCDataChannel;
7369
private publishedDataChannels: Map<string, RTCDataChannel> = new Map();
@@ -79,17 +75,14 @@ export class BandwidthRtc {
7975
private publishMutex: Mutex = new Mutex();
8076
private subscribeMutex: Mutex = new Mutex();
8177

82-
// Lookup maps for streams, keyed by mediastream id (msid)
8378
private publishedStreams: Map<string, PublishedStream> = new Map();
8479
private subscribedStreams: Map<string, StreamMetadata> = new Map();
8580

8681
// Current SDP revision for the subscribing peer; used to reject outdated SDP offers
8782
private subscribingPeerConnectionSdpRevision = 0;
8883

89-
// DTMF
9084
private localDtmfSenders: Map<string, RTCDTMFSender> = new Map();
9185

92-
// Event handlers
9386
private streamAvailableHandler?: { (event: RtcStream): void };
9487
private streamUnavailableHandler?: { (event: RtcStream): void };
9588
private readyHandler?: { (readyMetadata: ReadyMetadata): void };
@@ -456,7 +449,11 @@ export class BandwidthRtc {
456449
const publishOnTrackHandler = (event: RTCTrackEvent) => {
457450
logger.debug("publish ontrack event", event);
458451
};
459-
this.publishingPeerConnection = await this.setupPeerConnection(PEER_CONNECTION_TYPE_PUBLISH, publishOnTrackHandler, setMediaPreferencesResponse.publishSdpOffer.sdpOffer);
452+
this.publishingPeerConnection = await this.setupPeerConnection(
453+
PEER_CONNECTION_TYPE_PUBLISH,
454+
publishOnTrackHandler,
455+
setMediaPreferencesResponse.publishSdpOffer.sdpOffer,
456+
);
460457

461458
let streamTracks: Map<MediaStream, Set<MediaStreamTrack>> = new Map();
462459

@@ -531,31 +528,21 @@ export class BandwidthRtc {
531528
logger.debug("Setting up RTCPeerConnection");
532529
const peerConnection = this.createPeerConnection();
533530
this.setupNewPeerConnection(peerConnection, onTrack);
534-
// Attempt to restart ice if connection fails
535531
peerConnection.onconnectionstatechange = async (event: Event) => {
536532
try {
537533
const pc = event.target as RTCPeerConnection;
538-
let connectionState = pc.connectionState;
534+
const connectionState = pc.connectionState;
539535
logger.debug("onconnectionstatechange", connectionState, pc);
540536
if (connectionState === CONNECTION_STATE_FAILED) {
541537
logger.warn("Connection failed, attempting to restart ICE TODO");
542-
// await this.offerPublishSdp(true);
543-
// connectionState = pc.connectionState;
544-
// // TODO: add timeout so we dont loop here forever
545-
// while (connectionState === "failed") {
546-
// await new Promise((resolve) => setTimeout(resolve, 5000));
547-
// // Don't block on this, we should try multiple times
548-
// this.offerPublishSdp(true);
549-
// connectionState = pc.connectionState;
550-
// }
538+
// TODO: add timeout here
551539
}
552540
} catch (err) {
553541
if (globalThis.window) {
554542
logger.warn("onconnectionstatechange error", err);
555543
}
556544
}
557545
};
558-
// Do an initial sdp negotiation
559546
logger.debug("Initial SDP offer", initialSdpOffer);
560547
if (initialSdpOffer != undefined) {
561548
logger.debug("Setting initial SDP offer", initialSdpOffer);
@@ -579,8 +566,6 @@ export class BandwidthRtc {
579566
const dataChannel: RTCDataChannel = event.channel;
580567
if (dataChannel.label === HEARTBEAT_DATA_CHANNEL_LABEL) {
581568
logger.info("Heartbeat Data Channel opened", dataChannel);
582-
583-
// Handle heartbeat messages
584569
dataChannel.onmessage = (event) => {
585570
logger.debug("Heartbeat Data Channel message", event.data);
586571
if (event.data == HEARTBEAT_PING && dataChannel.readyState === DATA_CHANNEL_STATE_OPEN) {
@@ -592,8 +577,7 @@ export class BandwidthRtc {
592577
logger.info("Diagnostics Data Channel opened", dataChannel);
593578
} else {
594579
logger.info("Custom Data Channel opened", dataChannel);
595-
// // Custom data channel
596-
// this.subscribedDataChannels.set(dataChannel.label, dataChannel);
580+
// TODO: custom data channel
597581
}
598582
};
599583

@@ -681,7 +665,9 @@ export class BandwidthRtc {
681665
// present so that RTCDTMFSender can send RFC 4733 DTMF packets.
682666
const hasTelephoneEvent = codecPreferences.audio.some((c) => c.mimeType.toLowerCase() === TELEPHONE_EVENT_MIME_TYPE);
683667
if (!hasTelephoneEvent) {
684-
const telephoneEventCodec = RTCRtpSender.getCapabilities(TRACK_KIND_AUDIO)?.codecs.find((c) => c.mimeType.toLowerCase() === TELEPHONE_EVENT_MIME_TYPE);
668+
const telephoneEventCodec = RTCRtpSender.getCapabilities(TRACK_KIND_AUDIO)?.codecs.find(
669+
(c) => c.mimeType.toLowerCase() === TELEPHONE_EVENT_MIME_TYPE,
670+
);
685671
transceiver.setCodecPreferences(telephoneEventCodec ? [...codecPreferences.audio, telephoneEventCodec] : codecPreferences.audio);
686672
} else {
687673
transceiver.setCodecPreferences(codecPreferences.audio);

0 commit comments

Comments
 (0)