mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-07 16:17:20 +02:00
Stop using setCodecPreferences() as it causes stuttering on Chromium 124+
This commit is contained in:
parent
7aee4d5148
commit
889a97e56b
@ -2,7 +2,7 @@ import { BxEvent } from "@utils/bx-event";
|
|||||||
import { getPref, PrefKey } from "@utils/preferences";
|
import { getPref, PrefKey } from "@utils/preferences";
|
||||||
import { STATES } from "@utils/global";
|
import { STATES } from "@utils/global";
|
||||||
import { BxLogger } from "@utils/bx-logger";
|
import { BxLogger } from "@utils/bx-logger";
|
||||||
import { patchSdpBitrate } from "./sdp";
|
import { patchSdpBitrate, setCodecPreferences } from "./sdp";
|
||||||
import { StreamPlayer, type StreamPlayerOptions } from "@/modules/stream-player";
|
import { StreamPlayer, type StreamPlayerOptions } from "@/modules/stream-player";
|
||||||
|
|
||||||
export function patchVideoApi() {
|
export function patchVideoApi() {
|
||||||
@ -66,33 +66,6 @@ export function patchRtcCodecs() {
|
|||||||
if (typeof RTCRtpTransceiver === 'undefined' || !('setCodecPreferences' in RTCRtpTransceiver.prototype)) {
|
if (typeof RTCRtpTransceiver === 'undefined' || !('setCodecPreferences' in RTCRtpTransceiver.prototype)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const profilePrefix = codecProfile === 'high' ? '4d' : (codecProfile === 'low' ? '420' : '42e');
|
|
||||||
const profileLevelId = `profile-level-id=${profilePrefix}`;
|
|
||||||
|
|
||||||
const nativeSetCodecPreferences = RTCRtpTransceiver.prototype.setCodecPreferences;
|
|
||||||
RTCRtpTransceiver.prototype.setCodecPreferences = function(codecs) {
|
|
||||||
// Use the same codecs as desktop
|
|
||||||
const newCodecs = codecs.slice();
|
|
||||||
let pos = 0;
|
|
||||||
newCodecs.forEach((codec, i) => {
|
|
||||||
// Find high-quality codecs
|
|
||||||
if (codec.sdpFmtpLine && codec.sdpFmtpLine.includes(profileLevelId)) {
|
|
||||||
// Move it to the top of the array
|
|
||||||
newCodecs.splice(i, 1);
|
|
||||||
newCodecs.splice(pos, 0, codec);
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
nativeSetCodecPreferences.apply(this, [newCodecs]);
|
|
||||||
} catch (e) {
|
|
||||||
// Didn't work -> use default codecs
|
|
||||||
BxLogger.error('setCodecPreferences', e);
|
|
||||||
nativeSetCodecPreferences.apply(this, [codecs]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function patchRtcPeerConnection() {
|
export function patchRtcPeerConnection() {
|
||||||
@ -109,18 +82,27 @@ export function patchRtcPeerConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maxVideoBitrate = getPref(PrefKey.BITRATE_VIDEO_MAX);
|
const maxVideoBitrate = getPref(PrefKey.BITRATE_VIDEO_MAX);
|
||||||
if (maxVideoBitrate > 0) {
|
const codec = getPref(PrefKey.STREAM_CODEC_PROFILE);
|
||||||
|
|
||||||
|
if (codec !== 'default' || maxVideoBitrate > 0) {
|
||||||
const nativeSetLocalDescription = RTCPeerConnection.prototype.setLocalDescription;
|
const nativeSetLocalDescription = RTCPeerConnection.prototype.setLocalDescription;
|
||||||
RTCPeerConnection.prototype.setLocalDescription = function(description?: RTCLocalSessionDescriptionInit): Promise<void> {
|
RTCPeerConnection.prototype.setLocalDescription = function(description?: RTCLocalSessionDescriptionInit): Promise<void> {
|
||||||
|
// Set preferred codec profile
|
||||||
|
if (codec !== 'default') {
|
||||||
|
arguments[0].sdp = setCodecPreferences(arguments[0].sdp, codec);
|
||||||
|
}
|
||||||
|
|
||||||
// set maximum bitrate
|
// set maximum bitrate
|
||||||
try {
|
try {
|
||||||
if (description) {
|
if (maxVideoBitrate > 0 && description) {
|
||||||
arguments[0].sdp = patchSdpBitrate(arguments[0].sdp, Math.round(maxVideoBitrate / 1000));
|
arguments[0].sdp = patchSdpBitrate(arguments[0].sdp, Math.round(maxVideoBitrate / 1000));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
BxLogger.error('setLocalDescription', e);
|
BxLogger.error('setLocalDescription', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BxLogger.info('setLocalDescription', arguments[0].sdp);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return nativeSetLocalDescription.apply(this, arguments);
|
return nativeSetLocalDescription.apply(this, arguments);
|
||||||
};
|
};
|
||||||
|
@ -456,9 +456,6 @@ class XcloudInterceptor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
overrides.videoConfiguration = overrides.videoConfiguration || {};
|
|
||||||
overrides.videoConfiguration.setCodecPreferences = true;
|
|
||||||
|
|
||||||
// Enable touch controller
|
// Enable touch controller
|
||||||
if (TouchController.isEnabled()) {
|
if (TouchController.isEnabled()) {
|
||||||
overrides.inputConfiguration.enableTouchInput = true;
|
overrides.inputConfiguration.enableTouchInput = true;
|
||||||
|
@ -170,7 +170,7 @@ export class Preferences {
|
|||||||
default: t('default'),
|
default: t('default'),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!('getCapabilities' in RTCRtpReceiver) || typeof RTCRtpTransceiver === 'undefined' || !('setCodecPreferences' in RTCRtpTransceiver.prototype)) {
|
if (!('getCapabilities' in RTCRtpReceiver)) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,59 @@
|
|||||||
|
export function setCodecPreferences(sdp: string, preferredCodec: string) {
|
||||||
|
const h264Pattern = /a=fmtp:(\d+).*profile-level-id=([0-9a-f]{6})/g;
|
||||||
|
const profilePrefix = preferredCodec === 'high' ? '4d' : (preferredCodec === 'low' ? '420' : '42e');
|
||||||
|
|
||||||
|
const preferredCodecIds: string[] = [];
|
||||||
|
|
||||||
|
// Find all H.264 codec profile IDs
|
||||||
|
const matches = sdp.matchAll(h264Pattern) || [];
|
||||||
|
for (const match of matches) {
|
||||||
|
const id = match[1];
|
||||||
|
const profileId = match[2];
|
||||||
|
|
||||||
|
if (profileId.startsWith(profilePrefix)) {
|
||||||
|
preferredCodecIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No preferred IDs found
|
||||||
|
if (!preferredCodecIds.length) {
|
||||||
|
return sdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = sdp.split('\r\n');
|
||||||
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
||||||
|
const line = lines[lineIndex];
|
||||||
|
if (!line.startsWith('m=video')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc4566#section-5.14
|
||||||
|
// m=<media> <port> <proto> <fmt>
|
||||||
|
// m=video 9 UDP/TLS/RTP/SAVPF 127 39 102 104 106 108
|
||||||
|
const tmp = line.trim().split(' ');
|
||||||
|
|
||||||
|
// Get array of <fmt>
|
||||||
|
// ['127', '39', '102', '104', '106', '108']
|
||||||
|
let ids = tmp.slice(3);
|
||||||
|
|
||||||
|
// Remove preferred IDs in the original array
|
||||||
|
ids = ids.filter(item => !preferredCodecIds.includes(item));
|
||||||
|
|
||||||
|
// Put preferred IDs at the beginning
|
||||||
|
ids = preferredCodecIds.concat(ids);
|
||||||
|
|
||||||
|
// Update line's content
|
||||||
|
lines[lineIndex] = tmp.slice(0, 3).concat(ids).join(' ');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join('\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function patchSdpBitrate(sdp: string, video?: number, audio?: number) {
|
export function patchSdpBitrate(sdp: string, video?: number, audio?: number) {
|
||||||
const lines = sdp.split('\n');
|
const lines = sdp.split('\r\n');
|
||||||
|
|
||||||
const mediaSet: Set<string> = new Set();
|
const mediaSet: Set<string> = new Set();
|
||||||
!!video && mediaSet.add('video');
|
!!video && mediaSet.add('video');
|
||||||
@ -57,5 +111,5 @@ export function patchSdpBitrate(sdp: string, video?: number, audio?: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join('\n');
|
return lines.join('\r\n');
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user