Skip to content

Commit ba71908

Browse files
jpsantosbhiAmmar7
andauthored
Allow developers to control the audio codecs (#1190)
Co-authored-by: Ammar Ansari <[email protected]>
1 parent 5046808 commit ba71908

File tree

15 files changed

+581
-331
lines changed

15 files changed

+581
-331
lines changed

.changeset/cute-zebras-grin.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@signalwire/webrtc': minor
3+
'@signalwire/js': minor
4+
---
5+
6+
Added dial params:
7+
8+
- audioCodecs
9+
10+
The new parameter allows developers to select the audio codec to use with the desired configuration

internal/e2e-js/tests/roomSessionStreamingAPI.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ test.describe('Room Streaming from REST API', () => {
5252
const STREAM_CHECK_URL = process.env.STREAM_CHECK_URL!
5353
await pageTwo.goto(STREAM_CHECK_URL, { waitUntil: 'domcontentloaded' })
5454
await pageTwo.waitForSelector(`text=${streamName}`, { timeout: 10_000 })
55-
5655
await deleteRoom(roomData.id)
5756
})
5857
})

internal/playground-js/src/fabric/index.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
href="https://signalwire.com/assets/images/favicon.ico"
2727
/>
2828
</head>
29+
2930
<body class="bg-light">
3031
<div class="container">
3132
<div class="row pt-5">
@@ -104,7 +105,23 @@ <h5>Connect</h5>
104105
</label>
105106
</div>
106107
</div>
107-
<div class="d-grid gap-2">
108+
<div class="card">
109+
<div class="card-body">
110+
<div class="form-group">
111+
<label for="targetCodec">Audio Codec Override</label>
112+
<textarea
113+
type="text"
114+
rows="3"
115+
class="form-control"
116+
id=""
117+
placeholder="opus:maxplaybackrate=8000;maxaveragebitrate=4000\nPCMU:"
118+
onchange="saveInLocalStorage(event)"
119+
></textarea>
120+
</div>
121+
</div>
122+
</div>
123+
124+
<div class="d-grid gap-2 mt-3">
108125
<button
109126
id="btnConnect"
110127
class="btn btn-primary"

internal/playground-js/src/fabric/index.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,14 +307,20 @@ window.dial = async ({ reattach = false } = {}) => {
307307

308308
const dialer = reattach ? client.reattach : client.dial
309309

310-
const call = await dialer({
310+
const dialOptions = {
311311
nodeId: steeringId,
312312
to: document.getElementById('destination').value,
313313
rootElement: document.getElementById('rootElement'),
314314
fromFabricAddressId: document.getElementById('fromFabricAddressId').value,
315315
video: document.getElementById('video').checked,
316316
audio: document.getElementById('audio').checked,
317-
})
317+
}
318+
319+
if (document.getElementById('audioCodec').value.trim().length) {
320+
const userInput = document.getElementById('audioCodec').value.trim()
321+
dialOptions.audioCodecs = userInput.split('\n').map((codec) => codec.trim())
322+
}
323+
const call = await dialer(dialOptions)
318324

319325
window.__call = call
320326
roomObj = call
@@ -910,6 +916,12 @@ window.ready(async function () {
910916
document.getElementById('audio').checked = true
911917
document.getElementById('video').checked =
912918
localStorage.getItem('fabric.ws.video') === 'true'
919+
document.getElementById('targetCodec').value =
920+
localStorage.getItem('fabric.ws.targetCodec') || ''
921+
document.getElementById('maxPlaybackRate').value =
922+
localStorage.getItem('fabric.ws.maxPlaybackRate') || ''
923+
document.getElementById('maxAverageBitrate').value =
924+
localStorage.getItem('fabric.ws.maxAverageBitrate') || ''
913925

914926
const urlParams = new URLSearchParams(window.location.search)
915927
const room = urlParams.get('room')

package-lock.json

Lines changed: 33 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/src/types/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,11 @@ export type Promisify<T> = {
147147
export type Prettify<T> = NonNullable<unknown> & {
148148
[K in keyof T]: Prettify<T[K]>
149149
} & {}
150+
151+
export type AudioCodecOptions =
152+
| `opus:${string}`
153+
| `red:${string}`
154+
| `G722:${string}`
155+
| `PCMU:${string}`
156+
| `PCMA:${string}`
157+
| `telephone-event:${string}`

packages/js/src/fabric/WSClient.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export class WSClient extends BaseClient<{}> implements WSClientContract {
195195
disableUdpIceServers: params.disableUdpIceServers || false,
196196
userVariables: params.userVariables || this.wsClientOptions.userVariables,
197197
fromFabricAddressId: params.fromFabricAddressId,
198+
audioCodecs: params.audioCodecs,
198199
})
199200

200201
// WebRTC connection left the room.
@@ -232,6 +233,7 @@ export class WSClient extends BaseClient<{}> implements WSClientContract {
232233
prevCallId: payload.callID,
233234
disableUdpIceServers: params.disableUdpIceServers || false,
234235
userVariables: params.userVariables || this.wsClientOptions.userVariables,
236+
audioCodecs: params.audioCodecs
235237
})
236238

237239
// WebRTC connection left the room.

packages/js/src/fabric/interfaces/wsClient.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { SessionOptions, UserOptions } from '@signalwire/core'
1+
import {
2+
AudioCodecOptions,
3+
SessionOptions,
4+
UserOptions,
5+
} from '@signalwire/core'
26
import {
37
IncomingCallHandler,
48
IncomingCallHandlers,
@@ -117,6 +121,8 @@ export interface CallParams extends DefaultCallParams {
117121
stopMicrophoneWhileMuted?: boolean
118122
/** Fabric address ID matching one of the subscriber’s addresses to attribute conversation API events in the INVITE. */
119123
fromFabricAddressId?: string
124+
/** Audio codec with fmtp params */
125+
audioCodecs?: AudioCodecOptions[]
120126
}
121127

122128
export interface DialParams extends CallParams {

packages/webrtc/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@
3838
},
3939
"dependencies": {
4040
"@signalwire/core": "4.3.0-dev.202506111650.ad2f5be.0",
41-
"sdp": "^3.2.0"
41+
"sdp-transform": "2.15.0"
4242
},
43-
"types": "dist/cjs/webrtc/src/index.d.ts"
43+
"types": "dist/cjs/webrtc/src/index.d.ts",
44+
"devDependencies": {
45+
"@types/sdp-transform": "^2.4.9"
46+
}
4447
}

packages/webrtc/src/RTCPeer.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import {
55
filterIceServers,
66
} from './utils/helpers'
77
import {
8-
sdpStereoHack,
98
sdpBitrateHack,
109
sdpMediaOrderHack,
1110
sdpHasValidCandidates,
11+
useAudioCodecs,
12+
sdpStereoHack,
1213
} from './utils/sdpHelpers'
1314
import { BaseConnection } from './BaseConnection'
1415
import {
@@ -71,6 +72,8 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
7172
this.options
7273
)
7374

75+
this._validateOptions()
76+
7477
this._onIce = this._onIce.bind(this)
7578
this._onEndedTrackHandler = this._onEndedTrackHandler.bind(this)
7679

@@ -86,6 +89,16 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
8689
this.rtcConfigPolyfill = this.config
8790
}
8891

92+
private _validateOptions() {
93+
if (
94+
this.options.useStereo === true &&
95+
typeof this.options.audio === 'object' &&
96+
(this.options.audio.channelCount ?? 2) !== 2
97+
) {
98+
throw new Error('Mismatch params: useStereo, audio.channelCount')
99+
}
100+
}
101+
89102
get options() {
90103
return this.call.options
91104
}
@@ -820,6 +833,7 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
820833
googleMaxBitrate,
821834
googleMinBitrate,
822835
googleStartBitrate,
836+
audioCodecs,
823837
} = this.options
824838
if (localDescription.sdp && useStereo) {
825839
localDescription.sdp = sdpStereoHack(localDescription.sdp)
@@ -837,17 +851,15 @@ export default class RTCPeer<EventTypes extends EventEmitter.ValidEventTypes> {
837851
googleStartBitrate
838852
)
839853
}
840-
// this.logger.debug(
841-
// 'LOCAL SDP \n',
842-
// `Type: ${localDescription.type}`,
843-
// '\n\n',
844-
// localDescription.sdp
845-
// )
854+
if (localDescription.sdp && audioCodecs) {
855+
localDescription.sdp = useAudioCodecs(localDescription.sdp, audioCodecs)
856+
}
846857
return this.instance.setLocalDescription(localDescription)
847858
}
848859

849860
private _setRemoteDescription(remoteDescription: RTCSessionDescriptionInit) {
850-
if (remoteDescription.sdp && this.options.useStereo) {
861+
const { useStereo } = this.options
862+
if (remoteDescription.sdp && useStereo) {
851863
remoteDescription.sdp = sdpStereoHack(remoteDescription.sdp)
852864
}
853865
if (remoteDescription.sdp && this.instance.localDescription) {

0 commit comments

Comments
 (0)