mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 23:57:19 +02:00
205 lines
6.0 KiB
TypeScript
205 lines
6.0 KiB
TypeScript
import { BxEvent } from "@utils/bx-event";
|
|
import { getPref, PrefKey } from "@utils/preferences";
|
|
import { STATES } from "@utils/global";
|
|
import { BxLogger } from "@utils/bx-logger";
|
|
|
|
export function patchVideoApi() {
|
|
const PREF_SKIP_SPLASH_VIDEO = getPref(PrefKey.SKIP_SPLASH_VIDEO);
|
|
|
|
// Show video player when it's ready
|
|
const showFunc = function(this: HTMLVideoElement) {
|
|
this.style.visibility = 'visible';
|
|
this.removeEventListener('playing', showFunc);
|
|
|
|
if (!this.videoWidth) {
|
|
return;
|
|
}
|
|
|
|
BxEvent.dispatch(window, BxEvent.STREAM_PLAYING, {
|
|
$video: this,
|
|
});
|
|
}
|
|
|
|
const nativePlay = HTMLMediaElement.prototype.play;
|
|
HTMLMediaElement.prototype.play = function() {
|
|
if (this.className && this.className.startsWith('XboxSplashVideo')) {
|
|
if (PREF_SKIP_SPLASH_VIDEO) {
|
|
this.volume = 0;
|
|
this.style.display = 'none';
|
|
this.dispatchEvent(new Event('ended'));
|
|
|
|
return new Promise<void>(() => {});
|
|
}
|
|
|
|
return nativePlay.apply(this);
|
|
}
|
|
|
|
if (!!this.src) {
|
|
return nativePlay.apply(this);
|
|
}
|
|
|
|
this.addEventListener('playing', showFunc);
|
|
|
|
return nativePlay.apply(this);
|
|
};
|
|
}
|
|
|
|
|
|
export function patchRtcCodecs() {
|
|
const codecProfile = getPref(PrefKey.STREAM_CODEC_PROFILE);
|
|
if (codecProfile === 'default') {
|
|
return;
|
|
}
|
|
|
|
if (typeof RTCRtpTransceiver === 'undefined' || !('setCodecPreferences' in RTCRtpTransceiver.prototype)) {
|
|
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() {
|
|
const nativeCreateDataChannel = RTCPeerConnection.prototype.createDataChannel;
|
|
RTCPeerConnection.prototype.createDataChannel = function() {
|
|
// @ts-ignore
|
|
const dataChannel = nativeCreateDataChannel.apply(this, arguments);
|
|
|
|
BxEvent.dispatch(window, BxEvent.DATA_CHANNEL_CREATED, {
|
|
dataChannel: dataChannel,
|
|
});
|
|
|
|
return dataChannel;
|
|
}
|
|
|
|
const OrgRTCPeerConnection = window.RTCPeerConnection;
|
|
// @ts-ignore
|
|
window.RTCPeerConnection = function() {
|
|
const conn = new OrgRTCPeerConnection();
|
|
STATES.currentStream.peerConnection = conn;
|
|
|
|
conn.addEventListener('connectionstatechange', e => {
|
|
BxLogger.info('connectionstatechange', conn.connectionState);
|
|
});
|
|
return conn;
|
|
}
|
|
}
|
|
|
|
export function patchAudioContext() {
|
|
const OrgAudioContext = window.AudioContext;
|
|
const nativeCreateGain = OrgAudioContext.prototype.createGain;
|
|
|
|
// @ts-ignore
|
|
window.AudioContext = function(options?: AudioContextOptions | undefined): AudioContext {
|
|
const ctx = new OrgAudioContext(options);
|
|
BxLogger.info('patchAudioContext', ctx, options);
|
|
|
|
ctx.createGain = function() {
|
|
const gainNode = nativeCreateGain.apply(this);
|
|
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
|
|
|
|
STATES.currentStream.audioGainNode = gainNode;
|
|
return gainNode;
|
|
}
|
|
|
|
STATES.currentStream.audioContext = ctx;
|
|
return ctx;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable telemetry flags in meversion.js
|
|
*/
|
|
export function patchMeControl() {
|
|
const overrideConfigs = {
|
|
enableAADTelemetry: false,
|
|
enableTelemetry: false,
|
|
telEvs: '',
|
|
oneDSUrl: '',
|
|
};
|
|
|
|
const MSA = {
|
|
MeControl: {},
|
|
};
|
|
const MeControl = {};
|
|
|
|
const MsaHandler: ProxyHandler<any> = {
|
|
get(target, prop, receiver) {
|
|
return target[prop];
|
|
},
|
|
|
|
set(obj, prop, value) {
|
|
if (prop === 'MeControl' && value.Config) {
|
|
value.Config = Object.assign(value.Config, overrideConfigs);
|
|
}
|
|
|
|
obj[prop] = value;
|
|
return true;
|
|
},
|
|
};
|
|
|
|
const MeControlHandler: ProxyHandler<any> = {
|
|
get(target, prop, receiver) {
|
|
return target[prop];
|
|
},
|
|
|
|
set(obj, prop, value) {
|
|
if (prop === 'Config') {
|
|
value = Object.assign(value, overrideConfigs);
|
|
}
|
|
|
|
obj[prop] = value;
|
|
return true;
|
|
},
|
|
};
|
|
|
|
(window as any).MSA = new Proxy(MSA, MsaHandler);
|
|
(window as any).MeControl = new Proxy(MeControl, MeControlHandler);
|
|
}
|
|
|
|
/**
|
|
* Use power-saving flags for touch control
|
|
*/
|
|
export function patchCanvasContext() {
|
|
const nativeGetContext = HTMLCanvasElement.prototype.getContext;
|
|
// @ts-ignore
|
|
HTMLCanvasElement.prototype.getContext = function(contextType: string, contextAttributes?: any) {
|
|
if (contextType.includes('webgl')) {
|
|
contextAttributes = contextAttributes || {};
|
|
|
|
contextAttributes.antialias = false;
|
|
|
|
// Use low-power profile for touch controller
|
|
if (contextAttributes.powerPreference === 'high-performance') {
|
|
contextAttributes.powerPreference = 'low-power';
|
|
}
|
|
}
|
|
|
|
return nativeGetContext.apply(this, [contextType, contextAttributes]);
|
|
}
|
|
}
|