Skip to content

Commit

Permalink
Set max average bitrate on PTT calls (#2499)
Browse files Browse the repository at this point in the history
* Set max average bitrate on PTT calls

Via SDP munging. Also makes the SDP munging a bit more generic and
codec-specific (we were previously adding usedtx to any codec that had an fmtp
line already, which was probably not really the intention).

* Make SDP munging for codecs that don't already have fmtp lines

* Use sensible typescript syntax

Co-authored-by: Šimon Brandner <[email protected]>

Co-authored-by: Šimon Brandner <[email protected]>
  • Loading branch information
dbkr and SimonBrandner authored Jul 6, 2022
1 parent 9a15094 commit bdb91b3
Showing 1 changed file with 58 additions and 5 deletions.
63 changes: 58 additions & 5 deletions src/webrtc/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ interface AssertedIdentity {
displayName: string;
}

// Used internally to specify modifications to codec parameters in SDP
interface CodecParams {
enableDtx?: boolean; // true to enable discontinuous transmission, false to disable, undefined to leave as-is
maxAverageBitrate?: number; // sets the max average bitrate, or undefined to leave as-is
}

type CodecParamMods = Record<string, CodecParams>;

export enum CallState {
Fledgling = 'fledgling',
InviteSent = 'invite_sent',
Expand Down Expand Up @@ -261,6 +269,18 @@ export function genCallID(): string {
return Date.now().toString() + randomString(16);
}

function getCodecParamMods(isPtt: boolean): CodecParamMods {
const mods = {
'opus': {
enableDtx: true,
},
} as CodecParamMods;

if (isPtt) mods.opus.maxAverageBitrate = 12000;

return mods;
}

export type CallEventHandlerMap = {
[CallEvent.DataChannel]: (channel: RTCDataChannel) => void;
[CallEvent.FeedsChanged]: (feeds: CallFeed[]) => void;
Expand Down Expand Up @@ -1456,26 +1476,59 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap

// Enables DTX (discontinuous transmission) on the given session to reduce
// bandwidth when transmitting silence
private enableDtx(description: RTCSessionDescriptionInit): void {
private mungeSdp(description: RTCSessionDescriptionInit, mods: CodecParamMods): void {
// The only way to enable DTX at this time is through SDP munging
const sdp = parseSdp(description.sdp);

sdp.media.forEach(media => {
if (media.type === "audio") {
media.fmtp.forEach(fmtp => fmtp.config += ";usedtx=1");
const payloadTypeToCodecMap = new Map<number, string>();
const codecToPayloadTypeMap = new Map<string, number>();
for (const rtp of media.rtp) {
payloadTypeToCodecMap.set(rtp.payload, rtp.codec);
codecToPayloadTypeMap.set(rtp.codec, rtp.payload);
}

for (const [codec, params] of Object.entries(mods)) {
if (!codecToPayloadTypeMap.has(codec)) {
logger.info(`Ignoring SDP modifications for ${codec} as it's not present.`);
continue;
}

const extraconfig: string[] = [];
if (params.enableDtx !== undefined) {
extraconfig.push(`usedtx=${params.enableDtx ? '1' : '0'}`);
}
if (params.maxAverageBitrate !== undefined) {
extraconfig.push(`maxaveragebitrate=${params.maxAverageBitrate}`);
}

let found = false;
for (const fmtp of media.fmtp) {
if (payloadTypeToCodecMap.get(fmtp.payload) === codec) {
found = true;
fmtp.config += ";" + extraconfig.join(";");
}
}
if (!found) {
media.fmtp.push({
payload: codecToPayloadTypeMap.get(codec),
config: extraconfig.join(";"),
});
}
}
});
description.sdp = writeSdp(sdp);
}

private async createOffer(): Promise<RTCSessionDescriptionInit> {
const offer = await this.peerConn.createOffer();
this.enableDtx(offer);
this.mungeSdp(offer, getCodecParamMods(this.isPtt));
return offer;
}

private async createAnswer(): Promise<RTCSessionDescriptionInit> {
const answer = await this.peerConn.createAnswer();
this.enableDtx(answer);
this.mungeSdp(answer, getCodecParamMods(this.isPtt));
return answer;
}

Expand Down

0 comments on commit bdb91b3

Please sign in to comment.