mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 07:37:19 +02:00
Add "Maximum video bitrate" option
This commit is contained in:
parent
c1b41663db
commit
1dee720f77
@ -3,7 +3,7 @@
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
font-family: var(--bx-monospaced-font);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
@ -27,6 +27,9 @@ const SETTINGS_UI = {
|
||||
items: [
|
||||
PrefKey.STREAM_TARGET_RESOLUTION,
|
||||
PrefKey.STREAM_CODEC_PROFILE,
|
||||
|
||||
PrefKey.BITRATE_VIDEO_MAX,
|
||||
|
||||
PrefKey.AUDIO_ENABLE_VOLUME_CONTROL,
|
||||
PrefKey.AUDIO_MIC_ON_PLAYING,
|
||||
PrefKey.STREAM_DISABLE_FEEDBACK_DIALOG,
|
||||
|
@ -2,6 +2,7 @@ import { BxEvent } from "@utils/bx-event";
|
||||
import { getPref, PrefKey } from "@utils/preferences";
|
||||
import { STATES } from "@utils/global";
|
||||
import { BxLogger } from "@utils/bx-logger";
|
||||
import { patchSdpBitrate } from "./sdp";
|
||||
|
||||
export function patchVideoApi() {
|
||||
const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.SKIP_SPLASH_VIDEO);
|
||||
@ -96,6 +97,22 @@ export function patchRtcPeerConnection() {
|
||||
return dataChannel;
|
||||
}
|
||||
|
||||
const nativeSetLocalDescription = RTCPeerConnection.prototype.setLocalDescription;
|
||||
RTCPeerConnection.prototype.setLocalDescription = function(description?: RTCLocalSessionDescriptionInit): Promise<void> {
|
||||
// set maximum bitrate
|
||||
try {
|
||||
const maxVideoBitrate = getPref(PrefKey.BITRATE_VIDEO_MAX);
|
||||
if (maxVideoBitrate > 0) {
|
||||
arguments[0].sdp = patchSdpBitrate(arguments[0].sdp, maxVideoBitrate * 1000);
|
||||
}
|
||||
} catch (e) {
|
||||
BxLogger.error('setLocalDescription', e);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return nativeSetLocalDescription.apply(this, arguments);
|
||||
};
|
||||
|
||||
const OrgRTCPeerConnection = window.RTCPeerConnection;
|
||||
// @ts-ignore
|
||||
window.RTCPeerConnection = function() {
|
||||
|
@ -32,6 +32,8 @@ export enum PrefKey {
|
||||
|
||||
STREAM_DISABLE_FEEDBACK_DIALOG = 'stream_disable_feedback_dialog',
|
||||
|
||||
BITRATE_VIDEO_MAX = 'bitrate_video_max',
|
||||
|
||||
GAME_BAR_POSITION = 'game_bar_position',
|
||||
|
||||
LOCAL_CO_OP_ENABLED = 'local_co_op_enabled',
|
||||
@ -314,6 +316,28 @@ export class Preferences {
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.BITRATE_VIDEO_MAX]: {
|
||||
type: SettingElementType.NUMBER_STEPPER,
|
||||
label: 'Maximum video bitrate',
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 15,
|
||||
steps: 1,
|
||||
params: {
|
||||
suffix: ' Mb/s',
|
||||
exactTicks: 5,
|
||||
customTextValue: (value: any) => {
|
||||
value = parseInt(value);
|
||||
|
||||
if (value === 0) {
|
||||
return t('default');
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.GAME_BAR_POSITION]: {
|
||||
label: t('position'),
|
||||
default: 'bottom-left',
|
||||
|
61
src/utils/sdp.ts
Normal file
61
src/utils/sdp.ts
Normal file
@ -0,0 +1,61 @@
|
||||
export function patchSdpBitrate(sdp: string, video?: number, audio?: number) {
|
||||
const lines = sdp.split('\n');
|
||||
|
||||
const mediaSet: Set<string> = new Set();
|
||||
!!video && mediaSet.add('video');
|
||||
!!audio && mediaSet.add('audio');
|
||||
|
||||
const bitrate = {
|
||||
video,
|
||||
audio,
|
||||
};
|
||||
|
||||
for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
|
||||
let media: string = '';
|
||||
|
||||
let line = lines[lineNumber];
|
||||
if (!line.startsWith('m=')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const m of mediaSet) {
|
||||
if (line.startsWith(`m=${m}`)) {
|
||||
media = m;
|
||||
// Remove matched media from set
|
||||
mediaSet.delete(media);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid media, continue looking
|
||||
if (!media) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bLine = `b=AS:${bitrate[media as keyof typeof bitrate]}`;
|
||||
|
||||
while (lineNumber++, lineNumber < lines.length) {
|
||||
line = lines[lineNumber];
|
||||
// Ignore lines that start with "i=" or "c="
|
||||
if (line.startsWith('i=') || line.startsWith('c=')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith('b=AS:')) {
|
||||
// Replace bitrate
|
||||
lines[lineNumber] = bLine;
|
||||
// Stop lookine for "b=AS:" line
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.startsWith('m=')) {
|
||||
// "b=AS:" line not found, add "b" line before "m="
|
||||
lines.splice(lineNumber, 0, bLine);
|
||||
// Stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
@ -12,6 +12,8 @@ type NumberStepperParams = {
|
||||
|
||||
ticks?: number;
|
||||
exactTicks?: number;
|
||||
|
||||
customTextValue?: (value: any) => string | null;
|
||||
}
|
||||
|
||||
export enum SettingElementType {
|
||||
@ -131,9 +133,24 @@ export class SettingElement {
|
||||
const MAX = setting.max!;
|
||||
const STEPS = Math.max(setting.steps || 1, 1);
|
||||
|
||||
const renderTextValue = (value: any) => {
|
||||
value = parseInt(value as string);
|
||||
|
||||
let textContent = null;
|
||||
if (options.customTextValue) {
|
||||
textContent = options.customTextValue(value);
|
||||
}
|
||||
|
||||
if (textContent === null) {
|
||||
textContent = value.toString() + options.suffix;
|
||||
}
|
||||
|
||||
return textContent;
|
||||
};
|
||||
|
||||
const $wrapper = CE('div', {'class': 'bx-number-stepper'},
|
||||
$decBtn = CE('button', {'data-type': 'dec'}, '-') as HTMLButtonElement,
|
||||
$text = CE('span', {}, value + options.suffix) as HTMLSpanElement,
|
||||
$text = CE('span', {}, renderTextValue(value)) as HTMLSpanElement,
|
||||
$incBtn = CE('button', {'data-type': 'inc'}, '+') as HTMLButtonElement,
|
||||
);
|
||||
|
||||
@ -141,8 +158,7 @@ export class SettingElement {
|
||||
$range = CE('input', {'type': 'range', 'min': MIN, 'max': MAX, 'value': value, 'step': STEPS}) as HTMLInputElement;
|
||||
$range.addEventListener('input', e => {
|
||||
value = parseInt((e.target as HTMLInputElement).value);
|
||||
|
||||
$text.textContent = value + options.suffix;
|
||||
$text.textContent = renderTextValue(value);
|
||||
onChange && onChange(e, value);
|
||||
});
|
||||
$wrapper.appendChild($range);
|
||||
@ -204,7 +220,7 @@ export class SettingElement {
|
||||
value = Math.min(MAX, value + STEPS);
|
||||
}
|
||||
|
||||
$text.textContent = value.toString() + options.suffix;
|
||||
$text.textContent = renderTextValue(value);
|
||||
$range && ($range.value = value.toString());
|
||||
|
||||
isHolding = false;
|
||||
@ -237,7 +253,7 @@ export class SettingElement {
|
||||
|
||||
// Custom method
|
||||
($wrapper as any).setValue = (value: any) => {
|
||||
$text.textContent = value + options.suffix;
|
||||
$text.textContent = renderTextValue(value);
|
||||
$range && ($range.value = value);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user