Add "Limit video player's FPS" feature

This commit is contained in:
redphx 2024-10-12 16:15:51 +07:00
parent 0164423e45
commit 0c34173815
9 changed files with 130 additions and 11 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
display: inline-block; display: inline-block;
min-width: 40px; min-width: 40px;
font-family: var(--bx-monospaced-font); font-family: var(--bx-monospaced-font);
font-size: 12px; font-size: 13px;
margin: 0 4px; margin: 0 4px;
} }

View File

@ -75,6 +75,7 @@ export enum PrefKey {
VIDEO_PLAYER_TYPE = 'video_player_type', VIDEO_PLAYER_TYPE = 'video_player_type',
VIDEO_PROCESSING = 'video_processing', VIDEO_PROCESSING = 'video_processing',
VIDEO_POWER_PREFERENCE = 'video_power_preference', VIDEO_POWER_PREFERENCE = 'video_power_preference',
VIDEO_MAX_FPS = 'video_max_fps',
VIDEO_SHARPNESS = 'video_sharpness', VIDEO_SHARPNESS = 'video_sharpness',
VIDEO_RATIO = 'video_ratio', VIDEO_RATIO = 'video_ratio',
VIDEO_BRIGHTNESS = 'video_brightness', VIDEO_BRIGHTNESS = 'video_brightness',

View File

@ -25,6 +25,10 @@ export class WebGL2Player {
saturation: 0.0, saturation: 0.0,
}; };
private targetFps = 60;
private frameInterval = Math.ceil(1000 / this.targetFps);
private lastFrameTime = 0;
private animFrameId: number | null = null; private animFrameId: number | null = null;
constructor($video: HTMLVideoElement) { constructor($video: HTMLVideoElement) {
@ -67,6 +71,11 @@ export class WebGL2Player {
update && this.updateCanvas(); update && this.updateCanvas();
} }
setTargetFps(target: number) {
this.targetFps = target;
this.frameInterval = Math.ceil(1000 / target);
}
getCanvas() { getCanvas() {
return this.$canvas; return this.$canvas;
} }
@ -85,6 +94,16 @@ export class WebGL2Player {
} }
drawFrame() { drawFrame() {
// Limit FPS
if (this.targetFps < 60) {
const currentTime = performance.now();
const timeSinceLastFrame = currentTime - this.lastFrameTime;
if (timeSinceLastFrame < this.frameInterval) {
return;
}
this.lastFrameTime = currentTime;
}
const gl = this.gl!; const gl = this.gl!;
const $video = this.$video; const $video = this.$video;

View File

@ -7,9 +7,10 @@ import { getPref, setPref } from "@/utils/settings-storages/global-settings-stor
export function onChangeVideoPlayerType() { export function onChangeVideoPlayerType() {
const playerType = getPref(PrefKey.VIDEO_PLAYER_TYPE); const playerType = getPref(PrefKey.VIDEO_PLAYER_TYPE);
const $videoProcessing = document.getElementById('bx_setting_video_processing') as HTMLSelectElement; const $videoProcessing = document.getElementById(`bx_setting_${PrefKey.VIDEO_PROCESSING}`) as HTMLSelectElement;
const $videoSharpness = document.getElementById('bx_setting_video_sharpness') as HTMLElement; const $videoSharpness = document.getElementById(`bx_setting_${PrefKey.VIDEO_SHARPNESS}`) as HTMLElement;
const $videoPowerPreference = document.getElementById('bx_setting_video_power_preference') as HTMLElement; const $videoPowerPreference = document.getElementById(`bx_setting_${PrefKey.VIDEO_POWER_PREFERENCE}`) as HTMLElement;
const $videoMaxFps = document.getElementById(`bx_setting_${PrefKey.VIDEO_MAX_FPS}`) as HTMLElement;
if (!$videoProcessing) { if (!$videoProcessing) {
return; return;
@ -38,17 +39,27 @@ export function onChangeVideoPlayerType() {
// Hide Power Preference setting if renderer isn't WebGL2 // Hide Power Preference setting if renderer isn't WebGL2
$videoPowerPreference.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2); $videoPowerPreference.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2);
$videoMaxFps.closest('.bx-settings-row')!.classList.toggle('bx-gone', playerType !== StreamPlayerType.WEBGL2);
updateVideoPlayer(); updateVideoPlayer();
} }
export function limitVideoPlayerFps() {
const targetFps = getPref(PrefKey.VIDEO_MAX_FPS);
const streamPlayer = STATES.currentStream.streamPlayer;
streamPlayer?.getWebGL2Player()?.setTargetFps(targetFps);
}
export function updateVideoPlayer() { export function updateVideoPlayer() {
const streamPlayer = STATES.currentStream.streamPlayer; const streamPlayer = STATES.currentStream.streamPlayer;
if (!streamPlayer) { if (!streamPlayer) {
return; return;
} }
limitVideoPlayerFps();
const options = { const options = {
processing: getPref(PrefKey.VIDEO_PROCESSING), processing: getPref(PrefKey.VIDEO_PROCESSING),
sharpness: getPref(PrefKey.VIDEO_SHARPNESS), sharpness: getPref(PrefKey.VIDEO_SHARPNESS),
@ -60,6 +71,7 @@ export function updateVideoPlayer() {
streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE)); streamPlayer.setPlayerType(getPref(PrefKey.VIDEO_PLAYER_TYPE));
streamPlayer.updateOptions(options); streamPlayer.updateOptions(options);
streamPlayer.refreshPlayer(); streamPlayer.refreshPlayer();
} }
window.addEventListener('resize', updateVideoPlayer); window.addEventListener('resize', updateVideoPlayer);

View File

@ -1,6 +1,6 @@
import { isFullVersion } from "@macros/build" with {type: "macro"}; import { isFullVersion } from "@macros/build" with {type: "macro"};
import { onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils"; import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils";
import { ButtonStyle, CE, createButton, createSvgIcon, removeChildElements, type BxButton } from "@/utils/html"; import { ButtonStyle, CE, createButton, createSvgIcon, removeChildElements, type BxButton } from "@/utils/html";
import { NavigationDialog, NavigationDirection } from "./navigation-dialog"; import { NavigationDialog, NavigationDirection } from "./navigation-dialog";
import { ControllerShortcut } from "@/modules/controller-shortcut"; import { ControllerShortcut } from "@/modules/controller-shortcut";
@ -407,6 +407,9 @@ export class SettingsNavigationDialog extends NavigationDialog {
items: [{ items: [{
pref: PrefKey.VIDEO_PLAYER_TYPE, pref: PrefKey.VIDEO_PLAYER_TYPE,
onChange: onChangeVideoPlayerType, onChange: onChangeVideoPlayerType,
}, {
pref: PrefKey.VIDEO_MAX_FPS,
onChange: limitVideoPlayerFps,
}, { }, {
pref: PrefKey.VIDEO_POWER_PREFERENCE, pref: PrefKey.VIDEO_POWER_PREFERENCE,
onChange: () => { onChange: () => {

View File

@ -616,6 +616,21 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
highest: 'low-power', highest: 'low-power',
}, },
}, },
[PrefKey.VIDEO_MAX_FPS]: {
label: t('max-fps'),
type: SettingElementType.NUMBER_STEPPER,
default: 60,
min: 10,
max: 60,
steps: 10,
params: {
exactTicks: 10,
customTextValue: (value: any) => {
value = parseInt(value);
return value === 60 ? t('unlimited') : value + 'FPS';
},
},
},
[PrefKey.VIDEO_SHARPNESS]: { [PrefKey.VIDEO_SHARPNESS]: {
label: t('sharpness'), label: t('sharpness'),
type: SettingElementType.NUMBER_STEPPER, type: SettingElementType.NUMBER_STEPPER,

View File

@ -143,6 +143,7 @@ const Texts = {
"local-co-op": "Local co-op", "local-co-op": "Local co-op",
"lowest-quality": "Lowest quality", "lowest-quality": "Lowest quality",
"map-mouse-to": "Map mouse to", "map-mouse-to": "Map mouse to",
"max-fps": "Max FPS",
"may-not-work-properly": "May not work properly!", "may-not-work-properly": "May not work properly!",
"menu": "Menu", "menu": "Menu",
"microphone": "Microphone", "microphone": "Microphone",