mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-17 13:03:03 +02:00
159 lines
5.1 KiB
TypeScript
159 lines
5.1 KiB
TypeScript
import { BxEvent } from "./bx-event";
|
|
import { getPref, PrefKey } from "./preferences";
|
|
import { STATES } from "./global";
|
|
import { UserAgent } from "./user-agent";
|
|
|
|
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
|
|
console.log(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 => {
|
|
if (conn.connectionState === 'connecting') {
|
|
STATES.currentStream.audioGainNode = null;
|
|
}
|
|
console.log('connectionState', conn.connectionState);
|
|
});
|
|
return conn;
|
|
}
|
|
}
|
|
|
|
export function patchAudioContext() {
|
|
if (UserAgent.isSafari(true)) {
|
|
const nativeCreateGain = window.AudioContext.prototype.createGain;
|
|
window.AudioContext.prototype.createGain = function() {
|
|
const gainNode = nativeCreateGain.apply(this);
|
|
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
|
|
STATES.currentStream.audioGainNode = gainNode;
|
|
return gainNode;
|
|
}
|
|
}
|
|
|
|
const OrgAudioContext = window.AudioContext;
|
|
// @ts-ignore
|
|
window.AudioContext = function() {
|
|
const ctx = new OrgAudioContext();
|
|
STATES.currentStream.audioContext = ctx;
|
|
STATES.currentStream.audioGainNode = null;
|
|
return ctx;
|
|
}
|
|
|
|
const nativePlay = HTMLAudioElement.prototype.play;
|
|
HTMLAudioElement.prototype.play = function() {
|
|
this.muted = true;
|
|
|
|
const promise = nativePlay.apply(this);
|
|
if (STATES.currentStream.audioGainNode) {
|
|
return promise;
|
|
}
|
|
|
|
this.addEventListener('playing', e => (e.target as HTMLAudioElement).pause());
|
|
|
|
const audioCtx = STATES.currentStream.audioContext!;
|
|
// TOOD: check srcObject
|
|
const audioStream = audioCtx.createMediaStreamSource(this.srcObject as any);
|
|
const gainNode = audioCtx.createGain();
|
|
|
|
audioStream.connect(gainNode);
|
|
gainNode.connect(audioCtx.destination);
|
|
gainNode.gain.value = getPref(PrefKey.AUDIO_VOLUME) / 100;
|
|
STATES.currentStream.audioGainNode = gainNode;
|
|
|
|
return promise;
|
|
}
|
|
}
|