From 9dc504a591209145ce9ee473fbfffdbd20d9e255 Mon Sep 17 00:00:00 2001 From: Jaya Allamsetty Date: Mon, 4 Mar 2024 17:53:41 -0500 Subject: [PATCH] fix(SDP): Negotiate only baseline H.264 codecs for p2p. Chrome on macOS recently started offering encoder for higher level (5.2) but decoder only for level 3.1. See https://issues.chromium.org/issues/324930413 Therefore, filter out all H.264 payload types with main and high profiles. Also, sort all H.264 payload types so that same pt is picked for both H.264 encoder and decoder. Fixes random black tile issues across different browsers when H.264 is the preferred codec for p2p. --- modules/RTC/TraceablePeerConnection.js | 18 ++++++--------- modules/sdp/SDPUtil.js | 31 ++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/modules/RTC/TraceablePeerConnection.js b/modules/RTC/TraceablePeerConnection.js index 30b927eae9..834e8a9a06 100644 --- a/modules/RTC/TraceablePeerConnection.js +++ b/modules/RTC/TraceablePeerConnection.js @@ -1385,16 +1385,12 @@ TraceablePeerConnection.prototype._mungeCodecOrder = function(description) { for (const codec of currentCodecs) { if (this.isP2P) { - // Strip the high profile H264 codecs on mobile clients as they deliver better quality at the expense - // of higher load. - if (codec === CodecMimeType.H264 && browser.isMobileDevice()) { - SDPUtil.stripCodec(mLine, codec, true /* high profile */); - } - - // There are multiple VP9 payload types generated by the browser, more payload types are added if the - // endpoint doesn't have a local video source. Therefore, strip all the high profile codec variants for - // VP9 so that only one payload type for VP9 is negotiated between the peers. - if (codec === CodecMimeType.VP9) { + // 1. Strip the high profile H264 codecs on all clients. macOS started offering encoder for H.264 level + // 5.2 but a decoder only for level 3.1. Therfore, strip all main and high level codecs for H.264. + // 2. There are multiple VP9 payload types generated by the browser, more payload types are added if the + // endpoint doesn't have a local video source. Therefore, strip all the high profile codec variants + // for VP9 so that only one payload type for VP9 is negotiated between the peers. + if (codec === CodecMimeType.H264 || codec === CodecMimeType.VP9) { SDPUtil.stripCodec(mLine, codec, true /* high profile */); } @@ -1407,7 +1403,7 @@ TraceablePeerConnection.prototype._mungeCodecOrder = function(description) { // Reorder the codecs based on the preferred settings. for (const codec of this.codecSettings.codecList.slice().reverse()) { - SDPUtil.preferCodec(mLine, codec); + SDPUtil.preferCodec(mLine, codec, this.isP2P); } } diff --git a/modules/sdp/SDPUtil.js b/modules/sdp/SDPUtil.js index 24e2dc5ea6..2e8a7b4bfb 100644 --- a/modules/sdp/SDPUtil.js +++ b/modules/sdp/SDPUtil.js @@ -617,10 +617,11 @@ const SDPUtil = { * Sets the given codecName as the preferred codec by moving it to the beginning * of the payload types list (modifies the given mline in place). All instances * of the codec are moved up. - * @param {object} mLine the mline object from an sdp as parsed by transform.parse - * @param {string} codecName the name of the preferred codec + * @param {object} mLine the mline object from an sdp as parsed by transform.parse. + * @param {string} codecName the name of the preferred codec. + * @param {boolean} sortPayloadTypes whether the payloadtypes need to be sorted for a given codec. */ - preferCodec(mline, codecName) { + preferCodec(mline, codecName, sortPayloadTypes = false) { if (!mline || !codecName) { return; } @@ -630,6 +631,28 @@ const SDPUtil = { .map(rtp => rtp.payload); if (matchingPayloadTypes) { + if (sortPayloadTypes && codecName === CodecMimeType.H264) { + // Move all the H.264 codecs with packetization-mode=0 to top of the list. + const payloadsWithMode0 = matchingPayloadTypes.filter(payload => { + const fmtp = mline.fmtp.find(item => item.payload === payload); + + if (fmtp) { + return fmtp.config.includes('packetization-mode=0'); + } + + return false; + }); + + for (const pt of payloadsWithMode0.reverse()) { + const idx = matchingPayloadTypes.findIndex(payloadType => payloadType === pt); + + if (idx >= 0) { + matchingPayloadTypes.splice(idx, 1); + matchingPayloadTypes.unshift(pt); + } + } + } + // Call toString() on payloads to get around an issue within SDPTransform that sets // payloads as a number, instead of a string, when there is only one payload. const payloadTypes @@ -686,7 +709,7 @@ const SDPUtil = { if (codec) { return codec.toLowerCase() === CodecMimeType.VP9 ? !item.config.includes('profile-id=0') - : item.config.includes('profile-level-id=64'); + : !item.config.includes('profile-level-id=42'); } return false;