gstwebrtc-api: expose API on consumer-session for munging stereo

We cannot do that by default as this is technically non-compliant,
so we need to expose API to let the user opt into it.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1754>
This commit is contained in:
Mathieu Duponchelle 2024-08-26 17:04:00 +02:00 committed by GStreamer Marge Bot
parent aae9d5c0e9
commit db026ad535
2 changed files with 47 additions and 0 deletions

View file

@ -384,6 +384,8 @@
if (session) { if (session) {
entryElement._consumerSession = session; entryElement._consumerSession = session;
session.mungeStereoHack = true;
session.addEventListener("error", (event) => { session.addEventListener("error", (event) => {
if (entryElement._consumerSession === session) { if (entryElement._consumerSession === session) {
console.error(event.message, event.error); console.error(event.message, event.error);

View file

@ -44,6 +44,7 @@ export default class ConsumerSession extends WebRTCSession {
this._streams = []; this._streams = [];
this._remoteController = null; this._remoteController = null;
this._pendingCandidates = []; this._pendingCandidates = [];
this._mungeStereoHack = false;
this._offerOptions = offerOptions; this._offerOptions = offerOptions;
@ -57,6 +58,16 @@ export default class ConsumerSession extends WebRTCSession {
}); });
} }
/**
* Defines whether the SDP should be munged in order to enable stereo with chrome.
* @param {boolean} enable - Enable or disable the hack, default is false
*/
set mungeStereoHack(enable) {
if (typeof (enable) !== "boolean") { return; }
this._mungeStereoHack = enable;
}
/** /**
* The array of remote media streams consumed locally through this WebRTC channel. * The array of remote media streams consumed locally through this WebRTC channel.
* @member {external:MediaStream[]} GstWebRTCAPI.ConsumerSession#streams * @member {external:MediaStream[]} GstWebRTCAPI.ConsumerSession#streams
@ -238,6 +249,36 @@ export default class ConsumerSession extends WebRTCSession {
} }
} }
// Work around Chrome not handling stereo Opus correctly.
// See
// https://chromium.googlesource.com/external/webrtc/+/194e3bcc53ffa3e98045934377726cb25d7579d2/webrtc/media/engine/webrtcvoiceengine.cc#302
// https://bugs.chromium.org/p/webrtc/issues/detail?id=8133
//
// Technically it's against the spec to modify the SDP
// but there's no other API for this and this seems to
// be the only possible workaround at this time.
mungeStereo(offerSdp, answerSdp) {
const stereoRegexp = /a=fmtp:.* sprop-stereo/g;
let stereoPayloads = new Set();
for (const m of offerSdp.matchAll(stereoRegexp)) {
const payloadMatch = m[0].match(/a=fmtp:(\d+) .*/);
if (payloadMatch) {
stereoPayloads.add(payloadMatch[1]);
}
}
for (const payload of stereoPayloads) {
const isStereoRegexp = new RegExp("a=fmtp:" + payload + ".*stereo");
const answerIsStereo = answerSdp.match(isStereoRegexp);
if (!answerIsStereo) {
answerSdp = answerSdp.replaceAll("a=fmtp:" + payload, "a=fmtp:" + payload + " stereo=1;");
}
}
return answerSdp;
}
onSessionPeerMessage(msg) { onSessionPeerMessage(msg) {
if ((this._state === SessionState.closed) || !this._comChannel || !this._sessionId) { if ((this._state === SessionState.closed) || !this._comChannel || !this._sessionId) {
return; return;
@ -268,6 +309,10 @@ export default class ConsumerSession extends WebRTCSession {
} }
}).then((desc) => { }).then((desc) => {
if (this._rtcPeerConnection && desc) { if (this._rtcPeerConnection && desc) {
if (this._mungeStereoHack) {
desc.sdp = this.mungeStereo(msg.sdp.sdp, desc.sdp);
}
return this._rtcPeerConnection.setLocalDescription(desc); return this._rtcPeerConnection.setLocalDescription(desc);
} else { } else {
return null; return null;