From d25b86caf40290310bfbc328f84e175e775dcf1f Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Tue, 12 Jul 2022 09:24:12 +0800 Subject: [PATCH] mung sdp for av1 bitrate setting (#314) * mung sdp for av1 bitrate setting * code clean * Update src/room/participant/LocalParticipant.ts Co-authored-by: lukasIO * Update src/room/participant/LocalParticipant.ts Co-authored-by: lukasIO Co-authored-by: lukasIO --- .changeset/lemon-ads-smoke.md | 5 +++ src/room/PCTransport.ts | 39 ++++++++++++++++++++++++ src/room/participant/LocalParticipant.ts | 15 +++++++++ src/room/participant/publishUtils.ts | 2 +- 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .changeset/lemon-ads-smoke.md diff --git a/.changeset/lemon-ads-smoke.md b/.changeset/lemon-ads-smoke.md new file mode 100644 index 0000000000..32168b27ac --- /dev/null +++ b/.changeset/lemon-ads-smoke.md @@ -0,0 +1,5 @@ +--- +'livekit-client': patch +--- + +apply av1 bitrate setting diff --git a/src/room/PCTransport.ts b/src/room/PCTransport.ts index bec2a1f48d..0b51adafde 100644 --- a/src/room/PCTransport.ts +++ b/src/room/PCTransport.ts @@ -1,6 +1,13 @@ import { debounce } from 'ts-debounce'; import log from '../logger'; +/** @internal */ +interface TrackBitrateInfo { + sid: string; + codec: string; + maxbr: number; +} + /** @internal */ export default class PCTransport { pc: RTCPeerConnection; @@ -11,6 +18,8 @@ export default class PCTransport { renegotiate: boolean = false; + trackBitrates: TrackBitrateInfo[] = []; + onOffer?: (offer: RTCSessionDescriptionInit) => void; constructor(config?: RTCConfiguration) { @@ -78,10 +87,40 @@ export default class PCTransport { // actually negotiate log.debug('starting to negotiate'); const offer = await this.pc.createOffer(options); + + // mung sdp for codec bitrate setting that can't apply by sendEncoding + this.trackBitrates.forEach((trackbr) => { + let sdp = offer.sdp ?? ''; + const sidIndex = sdp.search(new RegExp(`msid.* ${trackbr.sid}`)); + if (sidIndex < 0) { + return; + } + + const mlineStart = sdp.substring(0, sidIndex).lastIndexOf('m='); + const mlineEnd = sdp.indexOf('m=', sidIndex); + const mediaSection = sdp.substring(mlineStart, mlineEnd); + + const mungedMediaSection = mediaSection.replace( + new RegExp(`a=rtpmap:(\\d+) ${trackbr.codec}/\\d+`, 'i'), + `$&\r\na=fmtp:$1 x-google-max-bitrate=${trackbr.maxbr}`, + ); + sdp = sdp.substring(0, mlineStart) + mungedMediaSection + sdp.substring(mlineEnd); + offer.sdp = sdp; + }); + this.trackBitrates = []; + await this.pc.setLocalDescription(offer); this.onOffer(offer); } + setTrackCodecBitrate(sid: string, codec: string, maxbr: number) { + this.trackBitrates.push({ + sid, + codec, + maxbr, + }); + } + close() { this.pc.close(); } diff --git a/src/room/participant/LocalParticipant.ts b/src/room/participant/LocalParticipant.ts index 47aca3feef..5f732ce9c7 100644 --- a/src/room/participant/LocalParticipant.ts +++ b/src/room/participant/LocalParticipant.ts @@ -544,6 +544,14 @@ export default class LocalParticipant extends Participant { track.codec = opts.videoCodec; } + if (track.codec === 'av1' && encodings && encodings[0]?.maxBitrate) { + this.engine.publisher.setTrackCodecBitrate( + req.cid, + track.codec, + encodings[0].maxBitrate / 1000, + ); + } + this.engine.negotiate(); // store RTPSender @@ -642,6 +650,13 @@ export default class LocalParticipant extends Participant { this.setPreferredCodec(transceiver, track.kind, opts.videoCodec); track.setSimulcastTrackSender(opts.videoCodec, transceiver.sender); + if (videoCodec === 'av1' && encodings[0]?.maxBitrate) { + this.engine.publisher.setTrackCodecBitrate( + req.cid, + videoCodec, + encodings[0].maxBitrate / 1000, + ); + } this.engine.negotiate(); log.debug(`published ${opts.videoCodec} for track ${track.sid}`, { encodings, trackInfo: ti }); } diff --git a/src/room/participant/publishUtils.ts b/src/room/participant/publishUtils.ts index 9f1ed77789..6dc5a03eff 100644 --- a/src/room/participant/publishUtils.ts +++ b/src/room/participant/publishUtils.ts @@ -106,7 +106,7 @@ export function computeVideoEncodings( encodings.push({ rid: videoRids[2 - i], scaleResolutionDownBy: 2 ** i, - maxBitrate: videoEncoding ? videoEncoding.maxBitrate / 2 ** i : 0, + maxBitrate: videoEncoding ? videoEncoding.maxBitrate / 3 ** i : 0, /* @ts-ignore */ maxFramerate: original.encoding.maxFramerate, /* @ts-ignore */