mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-30 19:31:44 +02:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
2569e73018 | |||
4c790ac38d | |||
11c233e14e | |||
a81cb86140 | |||
c3de245545 | |||
a7ab506f0f | |||
8ecff6adae | |||
27ec45512d | |||
3a654b99cb | |||
a009cca866 | |||
19302ea444 | |||
3c4248c1c7 | |||
de834fcb80 | |||
baf2c2a35d | |||
522830a47d | |||
7207646379 | |||
597c150c77 | |||
e7980c186d | |||
0c38b54c38 |
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Platform (please complete the following information):**
|
||||
- OS: [e.g. Android]
|
||||
- Browser: [e.g. chrome, firefox]
|
||||
- Browser Version: [e.g. 100]
|
||||
- Better xCloud Version: [e.g. 1.4]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
56
README.md
56
README.md
@ -6,27 +6,28 @@ Give this project a 🌟 if you like it. Thank you 🙏.
|
||||
|
||||
## Features
|
||||
|
||||
<img width="500" alt="Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/8fb9f0ac-85f5-4e5a-9570-5a5e119e4fc1">
|
||||
<img width="500" alt="Video Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/ed219d50-02ab-40bd-95c5-a010956d77bf">
|
||||
<img width="475" alt="Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/ad687344-214d-4822-affe-21f1b1e105c8">
|
||||
<img width="475" alt="Video Settings UI" src="https://github.com/redphx/better-xcloud/assets/96280/ed219d50-02ab-40bd-95c5-a010956d77bf">
|
||||
|
||||
|
||||
- **Switch region of streaming server**
|
||||
> Connect to another server instead of the default one. Check [FAQ section](#faq) for some notes.
|
||||
> Connect to another server instead of the default one. Check the [**FAQ** section](#faq) for some notes.
|
||||
> Not working in Hermit ([#5](https://github.com/redphx/better-xcloud/issues/5)).
|
||||
- **Force 1080p stream**
|
||||
> By default you only get 1080p stream when playing on desktop.
|
||||
> This feature will give you 1080p stream even on mobile, without having to change User-Agent.
|
||||
> Not working in Hermit ([#5](https://github.com/redphx/better-xcloud/issues/5)).
|
||||
- **Force high quality codec <sup>(\*)</sup>**
|
||||
> Force xCloud to use the best streaming codec profile (same as desktop & TV). You don't have to change User-Agent anymore.
|
||||
- **Force high quality codec (if possible)<sup>(\*)</sup>**
|
||||
> Force xCloud to use the best streaming codec profile (same as desktop & TV) if possible. You don't have to change User-Agent anymore.
|
||||
> You should enable this feature even if you're on desktop.
|
||||
> Use more bandwidth & battery.
|
||||
> Comparison video with the setting ON & OFF: https://youtu.be/-9PuBJJSgR4
|
||||
> Disable if it causes crashes.
|
||||
- **Prefer IPv6 streaming server**
|
||||
> Might reduce latency
|
||||
- **Disable bandwidth checking**
|
||||
> xCloud won't reduce quality when the internet speed is slow
|
||||
- **🔥 Capture screenshot**
|
||||
> Exclusive to **Better xCloud**. Check the [**Capture screenshot** section](#capture-screenshot) for more info.
|
||||
- **Skip Xbox splash video**
|
||||
> Save 3 seconds
|
||||
- **Hide Dots icon while playing**
|
||||
@ -50,7 +51,7 @@ Give this project a 🌟 if you like it. Thank you 🙏.
|
||||
<sup>(\*)</sup> By default (for compatibility reasons) xCloud only uses high quality codec profile when you use Tizen TV or Chrome/Edge/Chromium browser on Chrome/MacOS. Enable this setting will give you the best experience no matter what platform & browser you're on.
|
||||
|
||||
## How to use
|
||||
1. Install [Tampermonkey extension](https://www.tampermonkey.net/) on suppported browsers. It's also available for Firefox on Android.
|
||||
1. Install [Tampermonkey extension](https://www.tampermonkey.net/) on suppported browsers.
|
||||
2. Install **Better xCloud**:
|
||||
- [Stable version](https://github.com/redphx/better-xcloud/releases/latest/download/better-xcloud.user.js)
|
||||
- [Dev version](https://github.com/redphx/better-xcloud/raw/main/better-xcloud.user.js)
|
||||
@ -64,18 +65,43 @@ To update manually, just install the script again (you won't lose your settings)
|
||||
✅ = confirmed to be working
|
||||
❓ = not yet tested
|
||||
❌ = not supported (mostly because of lacking Userscript/extension support)
|
||||
➖ = unavailable
|
||||
⚠️ = see custom notes
|
||||
| | Desktop | Android | iOS |
|
||||
|----------------------------------------|------------------|------------------|-----------------|
|
||||
| Chrome/Edge/Chromium variants | ✅ | ❌ | ❌ |
|
||||
| Firefox | ✅ | ✅ | ❌ |
|
||||
| Safari | ✅<sup>(1)</sup> | ❌ | ✅<sup>(2)</sup> |
|
||||
| [Hermit](https://hermit.chimbori.com) | ❌ | ⚠️<sup>(3)</sup> | ❌ |
|
||||
| | Desktop | Android/Android TV | iOS |
|
||||
|-----------------------------------------|:-----------------|:-------------------|:----------------|
|
||||
| Chrome/Edge/Chromium variants | ✅ | ❌ | ❌ |
|
||||
| Firefox | ✅ | ⚠️<sup>(1)</sup> | ❌ |
|
||||
| Safari | ✅<sup>(2)</sup> | ➖ | ✅<sup>(3)</sup> |
|
||||
| [Hermit](https://hermit.chimbori.com) | ➖ | ⚠️<sup>(4)</sup> | ➖ |
|
||||
| [Kiwi Browser](https://kiwibrowser.com) | ➖ | ✅ | ➖ |
|
||||
|
||||
Don't see your browser in the table? If it supports Tampermonkey/Userscript then the answer is likely **"YES"**.
|
||||
|
||||
<sup>1, 2</sup> Requires [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887) (free & open source).
|
||||
<sup>3</sup> NOT RECOMMENDED at the moment since its Userscript implementation is not working properly (see https://github.com/redphx/better-xcloud/issues/5 for full details). It's still my favorite app to play xCloud on because it's lightweight, supports Userscript (premium features, only $1.99) without having to install anything else. I built **Better xCloud** just so I could use it with Hermit.
|
||||
<sup>1</sup> Follow [this guide](https://support.mozilla.org/en-US/kb/find-and-install-add-ons-firefox-android) to install Tampermonkey on Firefox Android. Its Gamepad API doesn't work properly so it might not recognize your controller.
|
||||
<sup>2, 3</sup> Requires [Userscripts app](https://apps.apple.com/us/app/userscripts/id1463298887) (free & open source).
|
||||
<sup>4</sup> NOT RECOMMENDED at the moment since its Userscript implementation is not working properly (see https://github.com/redphx/better-xcloud/issues/5 for full details).
|
||||
|
||||
---
|
||||
- **Kiwi Browser** is the best choice on Android. All features work, it means you can get 1080p stream + high quality codec profile (the best possible quality).
|
||||
- **Better xCloud** also works on Android TV, but you'll have to sideload the browser APK and need a bluetooth mouse if you want to interact with the Settings.
|
||||
|
||||
## Capture screenshot
|
||||
- This feature is only available in **Better xCloud**.
|
||||
- Works on both desktop & mobile, but it's designed for mobile users.
|
||||
- It captures the current frame of the stream and save to a file. That means you won't get the raw quality like when you play on console, but it's still better than using the built-in screenshot feature on your phone.
|
||||
- Screenshot's resolution & quality depend on the quality of the stream at the moment.
|
||||
- Screenshot doesn't touch UI, notification bar... only the gameplay.
|
||||
- There might be a slight delay.
|
||||
|
||||
### How to capture screenshot
|
||||
1. Enable this feature in setting.
|
||||
2. Play a game.
|
||||
3. Tap once at the bottom left/right (depend on your setting) to show the Screenshot button.
|
||||
4. Tap on that button to capture screenshot.
|
||||
5. Screenshot will be saved by browser.
|
||||
6. You can double tap that corner to capture screenshot.
|
||||
|
||||
<img width="600" alt="Screenshot button" src="https://github.com/redphx/better-xcloud/assets/96280/a911b141-5dc0-450a-aeac-30d9cf202b44">
|
||||
|
||||
## FAQ
|
||||
1. **Will I get banned for using this?**
|
||||
|
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name Better xCloud
|
||||
// @namespace https://github.com/redphx
|
||||
// @version 1.4
|
||||
// @version 1.5
|
||||
// @description Improve Xbox Cloud Gaming (xCloud) experience
|
||||
// @author redphx
|
||||
// @license MIT
|
||||
@ -13,15 +13,17 @@
|
||||
// ==/UserScript==
|
||||
'use strict';
|
||||
|
||||
const SCRIPT_VERSION = '1.4';
|
||||
const SCRIPT_VERSION = '1.5';
|
||||
const SCRIPT_HOME = 'https://github.com/redphx/better-xcloud';
|
||||
|
||||
const SERVER_REGIONS = {};
|
||||
|
||||
var $STREAM_VIDEO;
|
||||
var $SCREENSHOT_CANVAS;
|
||||
var GAME_TITLE_ID;
|
||||
|
||||
class StreamStatus {
|
||||
static ipv6 = false;
|
||||
static dimension = {width: 0, height: 0};
|
||||
static resolution = {width: 0, height: 0};
|
||||
static hqCodec = false;
|
||||
static region = '';
|
||||
|
||||
@ -39,7 +41,7 @@ class StreamStatus {
|
||||
['region', StreamStatus.region, '#d7450b'],
|
||||
['server', StreamStatus.ipv6 ? 'IPv6' : 'IPv4', '#008746'],
|
||||
['quality', StreamStatus.hqCodec ? 'High' : 'Normal', '#007c8f'],
|
||||
['dimension', `${StreamStatus.dimension.width}x${StreamStatus.dimension.height}`, '#ff3977'],
|
||||
['resolution', `${StreamStatus.resolution.width}x${StreamStatus.resolution.height}`, '#ff3977'],
|
||||
];
|
||||
|
||||
const $wrapper = createElement('div', {'class': 'better_xcloud_badges'});
|
||||
@ -56,6 +58,7 @@ class Preferences {
|
||||
static get FORCE_1080P_STREAM() { return 'force_1080p_stream'; }
|
||||
static get USE_DESKTOP_CODEC() { return 'use_desktop_codec'; }
|
||||
|
||||
static get SCREENSHOT_BUTTON_POSITION() { return 'screenshot_button_position'; }
|
||||
static get BLOCK_TRACKING() { return 'block_tracking'; }
|
||||
static get BLOCK_SOCIAL_FEATURES() { return 'block_social_features'; }
|
||||
static get DISABLE_BANDWIDTH_CHECKING() { return 'disable_bandwidth_checking'; }
|
||||
@ -73,88 +76,71 @@ class Preferences {
|
||||
'id': Preferences.SERVER_REGION,
|
||||
'label': 'Region of streaming server',
|
||||
'default': 'default',
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.FORCE_1080P_STREAM,
|
||||
'label': 'Force 1080p stream',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.USE_DESKTOP_CODEC,
|
||||
'label': 'Force high quality codec',
|
||||
'label': 'Force high quality codec (if possible)',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.PREFER_IPV6_SERVER,
|
||||
'label': 'Prefer IPv6 streaming server',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.DISABLE_BANDWIDTH_CHECKING,
|
||||
'label': 'Disable bandwidth checking',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.SCREENSHOT_BUTTON_POSITION,
|
||||
'label': 'Screenshot button\'s position',
|
||||
'default': 'bottom-left',
|
||||
'options': {
|
||||
'bottom-left': 'Bottom Left',
|
||||
'bottom-right': 'Bottom Right',
|
||||
'none': 'Disable',
|
||||
},
|
||||
}, {
|
||||
'id': Preferences.SKIP_SPLASH_VIDEO,
|
||||
'label': 'Skip Xbox splash video',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.HIDE_DOTS_ICON,
|
||||
'label': 'Hide Dots icon while playing',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.REDUCE_ANIMATIONS,
|
||||
'label': 'Reduce UI animations',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.BLOCK_SOCIAL_FEATURES,
|
||||
'label': 'Disable social features',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.BLOCK_TRACKING,
|
||||
'label': 'Disable xCloud analytics',
|
||||
'default': false,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.VIDEO_FILL_FULL_SCREEN,
|
||||
'label': 'Stretch video to full screen',
|
||||
'default': false,
|
||||
'hidden': true,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.VIDEO_SATURATION,
|
||||
'label': 'Video saturation (%)',
|
||||
'default': 100,
|
||||
'min': 0,
|
||||
'max': 150,
|
||||
'hidden': true,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.VIDEO_CONTRAST,
|
||||
'label': 'Video contrast (%)',
|
||||
'default': 100,
|
||||
'min': 0,
|
||||
'max': 150,
|
||||
'hidden': true,
|
||||
},
|
||||
|
||||
{
|
||||
}, {
|
||||
'id': Preferences.VIDEO_BRIGHTNESS,
|
||||
'label': 'Video brightness (%)',
|
||||
'default': 100,
|
||||
@ -363,6 +349,37 @@ div[class*=StreamMenu-module__menuContainer] > div[class*=Menu-module] {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.better_xcloud_screenshot_button {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
padding: 5px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-origin: content-box;
|
||||
filter: drop-shadow(0 0 2px #000000B0);
|
||||
transition: opacity 0.1s ease-in-out 0s, padding 0.1s ease-in 0s;
|
||||
z-index: 8888;
|
||||
|
||||
/* Credit: https://www.iconfinder.com/iconsets/user-interface-outline-27 */
|
||||
background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIgeG1sbnM6dj0iaHR0cHM6Ly92ZWN0YS5pby9uYW5vIiBmaWxsPSIjZmZmIj48cGF0aCBkPSJNMTIgN2E1LjAyIDUuMDIgMCAwIDAtNSA1IDUuMDIgNS4wMiAwIDAgMCA1IDUgNS4wMiA1LjAyIDAgMCAwIDUtNSA1LjAyIDUuMDIgMCAwIDAtNS01em0wIDJjMS42NjkgMCAzIDEuMzMxIDMgM3MtMS4zMzEgMy0zIDMtMy0xLjMzMS0zLTMgMS4zMzEtMyAzLTN6TTYgMkMzLjgwMSAyIDIgMy44MDEgMiA2djJhMSAxIDAgMSAwIDIgMFY2YTEuOTcgMS45NyAwIDAgMSAyLTJoMmExIDEgMCAxIDAgMC0yek0zIDE1YTEgMSAwIDAgMC0xIDF2MmMwIDIuMTk5IDEuODAxIDQgNCA0aDJhMSAxIDAgMSAwIDAtMkg2YTEuOTcgMS45NyAwIDAgMS0yLTJ2LTJhMSAxIDAgMCAwLTEtMXptMTggMGExIDEgMCAwIDAtMSAxdjJhMS45NyAxLjk3IDAgMCAxLTIgMmgtMmExIDEgMCAxIDAgMCAyaDJjMi4xOTkgMCA0LTEuODAxIDQtNHYtMmExIDEgMCAwIDAtMS0xeiIvPjxwYXRoIGQ9Ik0xNiAyYTEgMSAwIDEgMCAwIDJoMmExLjk3IDEuOTcgMCAwIDEgMiAydjJhMSAxIDAgMSAwIDIgMFY2YzAtMi4xOTktMS44MDEtNC00LTR6Ii8+PC9zdmc+Cg==);
|
||||
}
|
||||
|
||||
.better_xcloud_screenshot_button[data-showing=true] {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.better_xcloud_screenshot_button[data-capturing=true] {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.better_xcloud_screenshot_canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide UI elements */
|
||||
#headerArea, #uhfSkipToMain, .uhf-footer {
|
||||
display: none;
|
||||
@ -720,27 +737,41 @@ function injectSettingsButton($parent) {
|
||||
|
||||
let $control;
|
||||
let labelAttrs = {};
|
||||
if (setting.id === Preferences.SERVER_REGION) {
|
||||
if (setting.id === Preferences.SERVER_REGION || setting.options) {
|
||||
let selectedValue;
|
||||
|
||||
$control = CE('select', {id: 'xcloud_setting_' + setting.id});
|
||||
$control.addEventListener('change', e => {
|
||||
PREFS.set(Preferences.SERVER_REGION, e.target.value);
|
||||
PREFS.set(setting.id, e.target.value);
|
||||
});
|
||||
|
||||
for (let regionName in SERVER_REGIONS) {
|
||||
const region = SERVER_REGIONS[regionName];
|
||||
let value = regionName;
|
||||
if (setting.id === Preferences.SERVER_REGION) {
|
||||
selectedValue = preferredRegion;
|
||||
setting.options = {};
|
||||
for (let regionName in SERVER_REGIONS) {
|
||||
const region = SERVER_REGIONS[regionName];
|
||||
let value = regionName;
|
||||
|
||||
let label = regionName;
|
||||
if (region.isDefault) {
|
||||
label += ' (Default)';
|
||||
value = 'default';
|
||||
let label = regionName;
|
||||
if (region.isDefault) {
|
||||
label += ' (Default)';
|
||||
value = 'default';
|
||||
}
|
||||
|
||||
setting.options[value] = label;
|
||||
}
|
||||
} else {
|
||||
selectedValue = PREFS.get(setting.id);
|
||||
}
|
||||
|
||||
for (let value in setting.options) {
|
||||
const label = setting.options[value];
|
||||
|
||||
const $option = CE('option', {value: value}, label);
|
||||
$option.selected = regionName === preferredRegion;
|
||||
|
||||
$option.selected = value === selectedValue || label.includes(selectedValue);
|
||||
$control.appendChild($option);
|
||||
}
|
||||
|
||||
} else {
|
||||
$control = CE('input', {
|
||||
id: 'xcloud_setting_' + setting.id,
|
||||
@ -972,6 +1003,7 @@ function injectVideoSettingsButton() {
|
||||
|
||||
function patchVideoApi() {
|
||||
const PREF_SKIP_SPLASH_VIDEO = PREFS.get(Preferences.SKIP_SPLASH_VIDEO);
|
||||
const PREF_SCREENSHOT_BUTTON_POSITION = PREFS.get(Preferences.SCREENSHOT_BUTTON_POSITION);
|
||||
|
||||
// Show video player when it's ready
|
||||
var showFunc;
|
||||
@ -980,7 +1012,23 @@ function patchVideoApi() {
|
||||
this.removeEventListener('playing', showFunc);
|
||||
|
||||
if (this.videoWidth) {
|
||||
StreamStatus.dimension = {width: this.videoWidth, height: this.videoHeight};
|
||||
$STREAM_VIDEO = this;
|
||||
$SCREENSHOT_CANVAS.width = this.videoWidth;
|
||||
$SCREENSHOT_CANVAS.height = this.videoHeight;
|
||||
StreamStatus.resolution = {width: this.videoWidth, height: this.videoHeight};
|
||||
|
||||
if (PREF_SCREENSHOT_BUTTON_POSITION !== 'none') {
|
||||
const $btn = document.querySelector('.better_xcloud_screenshot_button');
|
||||
$btn.style.display = 'block';
|
||||
|
||||
if (PREF_SCREENSHOT_BUTTON_POSITION === 'bottom-right') {
|
||||
$btn.style.right = '0';
|
||||
} else {
|
||||
$btn.style.left = '0';
|
||||
}
|
||||
}
|
||||
|
||||
GAME_TITLE_ID = /\/launch\/([^/]+)/.exec(window.location.pathname)[1];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1021,19 +1069,24 @@ function patchRtcCodecs() {
|
||||
RTCRtpTransceiver.prototype.orgSetCodecPreferences = RTCRtpTransceiver.prototype.setCodecPreferences;
|
||||
RTCRtpTransceiver.prototype.setCodecPreferences = function(codecs) {
|
||||
// Use the same codecs as desktop
|
||||
codecs = [
|
||||
{
|
||||
'clockRate': 90000,
|
||||
'mimeType': 'video/H264',
|
||||
'sdpFmtpLine': 'level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f',
|
||||
},
|
||||
{
|
||||
'clockRate': 90000,
|
||||
'mimeType': 'video/H264',
|
||||
'sdpFmtpLine': 'level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=4d001f',
|
||||
const newCodecs = codecs.slice();
|
||||
let pos = 0;
|
||||
newCodecs.forEach((codec, i) => {
|
||||
// Find high quality codecs
|
||||
if (codec.sdpFmtpLine && codec.sdpFmtpLine.includes('profile-level-id=4d')) {
|
||||
// Move it to the top of the array
|
||||
newCodecs.splice(i, 1);
|
||||
newCodecs.splice(pos, 0, codec);
|
||||
++pos;
|
||||
}
|
||||
].concat(codecs);
|
||||
this.orgSetCodecPreferences(codecs);
|
||||
});
|
||||
|
||||
try {
|
||||
this.orgSetCodecPreferences.apply(this, [newCodecs]);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.orgSetCodecPreferences.apply(this, [codecs]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1209,6 +1262,75 @@ function setupVideoSettingsBar() {
|
||||
}
|
||||
|
||||
|
||||
function setupScreenshotButton() {
|
||||
$SCREENSHOT_CANVAS = createElement('canvas', {'class': 'better_xcloud_screenshot_canvas'});
|
||||
document.documentElement.appendChild($SCREENSHOT_CANVAS);
|
||||
|
||||
const $canvasContext = $SCREENSHOT_CANVAS.getContext('2d');
|
||||
|
||||
const delay = 2000;
|
||||
const $btn = createElement('div', {'class': 'better_xcloud_screenshot_button', 'data-showing': false});
|
||||
|
||||
let timeout;
|
||||
const detectDbClick = e => {
|
||||
if (!$STREAM_VIDEO) {
|
||||
timeout = null;
|
||||
$btn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
$btn.setAttribute('data-capturing', 'true');
|
||||
|
||||
$canvasContext.drawImage($STREAM_VIDEO, 0, 0, $SCREENSHOT_CANVAS.width, $SCREENSHOT_CANVAS.height);
|
||||
$SCREENSHOT_CANVAS.toBlob(blob => {
|
||||
// Download screenshot
|
||||
const now = +new Date;
|
||||
const $anchor = createElement('a', {
|
||||
'download': `${GAME_TITLE_ID}-${now}.png`,
|
||||
'href': URL.createObjectURL(blob),
|
||||
});
|
||||
$anchor.click();
|
||||
|
||||
// Free screenshot from memory
|
||||
URL.revokeObjectURL($anchor.href);
|
||||
$canvasContext.clearRect(0, 0, $SCREENSHOT_CANVAS.width, $SCREENSHOT_CANVAS.height);
|
||||
|
||||
// Hide button
|
||||
$btn.setAttribute('data-showing', 'false');
|
||||
setTimeout(() => {
|
||||
if (!timeout) {
|
||||
$btn.setAttribute('data-capturing', 'false');
|
||||
}
|
||||
}, 100);
|
||||
}, 'image/png');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const isShowing = $btn.getAttribute('data-showing') === 'true';
|
||||
if (!isShowing) {
|
||||
// Show button
|
||||
$btn.setAttribute('data-showing', 'true');
|
||||
$btn.setAttribute('data-capturing', 'false');
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null;
|
||||
$btn.setAttribute('data-showing', 'false');
|
||||
$btn.setAttribute('data-capturing', 'false');
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
|
||||
$btn.addEventListener('mousedown', detectDbClick);
|
||||
document.documentElement.appendChild($btn);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function patchHistoryMethod(type) {
|
||||
var orig = window.history[type];
|
||||
return function(...args) {
|
||||
@ -1231,6 +1353,9 @@ function hideUiOnPageChange() {
|
||||
if ($quickBar) {
|
||||
$quickBar.style.display = 'none';
|
||||
}
|
||||
|
||||
$STREAM_VIDEO = null;
|
||||
document.querySelector('.better_xcloud_screenshot_button').style = '';
|
||||
}
|
||||
|
||||
|
||||
@ -1252,15 +1377,14 @@ if (PREFS.get(Preferences.DISABLE_BANDWIDTH_CHECKING)) {
|
||||
}
|
||||
|
||||
patchRtcCodecs();
|
||||
|
||||
interceptHttpRequests();
|
||||
|
||||
patchVideoApi();
|
||||
|
||||
// Setup UI
|
||||
addCss();
|
||||
updateVideoPlayerCss();
|
||||
setupVideoSettingsBar();
|
||||
setupScreenshotButton();
|
||||
|
||||
// Workaround for Hermit browser
|
||||
var onLoadTriggered = false;
|
||||
@ -1275,10 +1399,30 @@ if (document.readyState === 'complete' && !onLoadTriggered) {
|
||||
|
||||
RTCPeerConnection.prototype.orgSetRemoteDescription = RTCPeerConnection.prototype.setRemoteDescription;
|
||||
RTCPeerConnection.prototype.setRemoteDescription = function(...args) {
|
||||
StreamStatus.hqCodec = false;
|
||||
|
||||
const sdpDesc = args[0];
|
||||
if (sdpDesc.sdp) {
|
||||
StreamStatus.hqCodec = sdpDesc.sdp.includes('profile-level-id=4d');
|
||||
const sdp = sdpDesc.sdp;
|
||||
|
||||
let lineIndex = 0;
|
||||
let endPos = 0;
|
||||
let line;
|
||||
while (lineIndex > -1) {
|
||||
lineIndex = sdp.indexOf('a=fmtp:', endPos);
|
||||
if (lineIndex === -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
endPos = sdp.indexOf('\n', lineIndex);
|
||||
line = sdp.substring(lineIndex, endPos);
|
||||
if (line.includes('profile-level-id')) {
|
||||
StreamStatus.hqCodec = line.includes('profile-level-id=4d');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.orgSetRemoteDescription.apply(this, args);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user