@vestra-player/sdk 1.5.41 → 1.5.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -3
- package/README.md +7 -6
- package/dist/api-requests.js +1 -0
- package/dist/host-capabilities.js +2 -0
- package/dist/plugin/services.js +24 -2
- package/dist/plugin/testing.d.ts +65 -1
- package/dist/plugin/testing.js +106 -1
- package/dist/plugin/types.d.ts +219 -28
- package/dist/wire.d.ts +31 -4
- package/llms-full.txt +4851 -2554
- package/llms.txt +25 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.43
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody.** Additions only, `apiVersion` stays `vestra/1`, no new capability. The new API needs player 0.5.25 (`"minPlayerVersion": "0.5.25"`); older players leave the new fields out and refuse the new event (the listener never fires). The `decoding` reason code stays as it is: the cause is a new optional field beside it.
|
|
6
|
+
|
|
7
|
+
- **`UpscalingReason.cause`** (player 0.5.25): with code `decoding`, why the video is not decoded on the GPU that renders it, as a stable `UpscalingDecodingCause`: `copy-back` (decoded on the GPU and copied back to memory, usually because a plugin's video filter needs the frames there), `hwdec-off` (hardware decoding off), `software` (this video is decoded in software: a cover picture, an unsupported format), `other-hwdec` (another hardware decoder). A newer player may add causes. With `copy-back`, **`UpscalingReason.plugins`** lists whose video filters made the player switch (`UpscalingPlugin`: manifest `id` and `name`; empty when the copy-back mode is the user's own setting), and the message names them ("…because the video filter of Picture Adjustments needs the frames in memory").
|
|
8
|
+
- **Monitoring fields in `UpscalingState`** (player 0.5.25, optional: absent from older players): `video` (the video's stored and display size as the upscaler gets it, `UpscalingVideoSize`), `area` (the video area in pixels), `gpu` (the rendering GPU's name), `hwdecCurrent` (mpv's decoder for this video), `inChain` (the upscaler is in the filter chain now, read from the chain). The driver still gives no sign that it upscales.
|
|
9
|
+
- **`plugin.video.upscaling.onChange(listener)`** (player 0.5.25, `video.upscaling`): the state whenever it changes while some plugin has upscaling on (the player looks about five times a second and sends only differences), so a window can show it live without polling. Wire: event `video.upscaling.changed` with the state without `enabled` (the SDK adds this plugin's own); subscribing needs `video.upscaling`, not `events`. A listener keeps the plugin running (it is not an event the player wakes a stopped plugin for): listen only while a window shows the state. The manifest names in `plugins` and the message are not translated.
|
|
10
|
+
- **`TestHost`**: `upscalingDecoding(cause, plugins?)` fakes each cause with the player's message (and a matching `upscaling.hwdecCurrent`); `upscaling.video`, `area`, `gpu`, `hwdecCurrent` (defaults: 1080p on a 4K area, an RTX 4070, `d3d11va`; `inChain` follows `requested`); `upscalingChanged()` sends the event to a listening plugin.
|
|
11
|
+
- The player's capability table copy (`video.upscaling.changed` under `video.upscaling`) and `CAPABILITY_INFO` (`VideoUpscaling.onChange`) follow.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 1.5.42
|
|
15
|
+
|
|
16
|
+
**Who must change: nobody.** Documentation only: no API changes, `apiVersion` stays `vestra/1`, no player version needed. The package's code is the same as 1.5.41; the doc comments' `@see` links (27 in `src/plugin/types.ts`) now point at `docs/explanation/` instead of `docs/concepts/`. The site's page addresses stay the same, and the old _Concepts_ group address redirects to _Explanation_; `llms.txt` and `llms-full.txt` follow the new pages.
|
|
17
|
+
|
|
18
|
+
- **The documentation follows Diátaxis.** The site's groups are Tutorials, How-to, Reference, Explanation, Guidelines, Examples and Upgrading, each with one kind of page whose shape `npm run docs:check` enforces (a tutorial has `Before you start`, numbered steps and `Next`; a how-to guide a goal, steps, a complete example and `See also`; explanation pages give no steps). _Concepts_ became _Explanation_ (the old address redirects); the migration notes moved to _Upgrading_ (`docs/upgrading/`), with every release's "who must change" in one generated table. New pages: tutorials _Add a window_, _Add settings and translations_, _Test, check, pack and publish_; _Troubleshooting_; reference pages _Error codes_, _Translations_ and _Test host_ (generated tables); explanation pages _GPU and software rendering_, _The player's Rust core_ and _How the player runs and talks to plugins_. Diagrams are Mermaid, and a check refuses link syntax in their labels.
|
|
19
|
+
- **New tutorial _Write your own blur kernel_**: what the numbers of a 3×3 blur kernel do and why it is normalized; the kernel as an mpv user shader the plugin owns, applied with `plugin.video.shaders()`; the same kernel as a `convolution` node (the 9, 25 and 49 entry sizes, `row` and `column` kernels, `rdiv`, `bias` and `VIDEO_PROCESSING_LIMITS`) that also reaches processed snapshots; and `gblur` as the shortcut.
|
|
20
|
+
- **New how-to guide _Keep a command's state without keeping the plugin running_**: find what keeps a plugin running with `TestHost.keepsRunning`, replace functions that read `plugin.player.state` with `plugin.when` conditions, keep the plugin's own state as values, and test it.
|
|
21
|
+
- _Commands, menus and key bindings_ (explanation) explains `plugin.when`: why the player evaluates conditions, how they relate to idle stop, mirror and listening subscriptions, and `keepsRunning` / `listeningWhy`. _How a plugin runs_ says that listeners of `packages`, `bindings`, `appearance` and the resolvers' file types no longer keep a plugin running (player 0.5.21). _Video and audio processing_ describes the shader channel and the `convolution` and `lut3d` nodes.
|
|
22
|
+
- _The player's Rust core_ shows the current window renderer choice (player 0.5.20): Direct3D 12 windows in CPU mode on Windows and their fallback to software windows. No page says any longer that only macOS renders on the GPU.
|
|
23
|
+
|
|
3
24
|
## 1.5.41
|
|
4
25
|
|
|
5
26
|
**Who must change: nobody.** No API changes, `apiVersion` stays `vestra/1`, no player version needed. Only how the player draws the bottom bar's strip of command buttons changes (player 0.5.22); a plugin's declarations and a bar that places `BarButtons` stay as they are.
|
|
@@ -210,7 +231,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
210
231
|
|
|
211
232
|
## 1.5.17
|
|
212
233
|
|
|
213
|
-
**Package rollback is removed**, as announced in 1.5.16: the player keeps no previous plugin version. `apiVersion` stays `vestra/1` (a patch release, as when 1.5.8 removed `PackageCandidate.trial()`); plugins that do not manage other plugins need no change. A plugin manager that still calls `rollback` or reads `previousVersion` no longer compiles. Migration: [Migrating off package rollback](docs/
|
|
234
|
+
**Package rollback is removed**, as announced in 1.5.16: the player keeps no previous plugin version. `apiVersion` stays `vestra/1` (a patch release, as when 1.5.8 removed `PackageCandidate.trial()`); plugins that do not manage other plugins need no change. A plugin manager that still calls `rollback` or reads `previousVersion` no longer compiles. Migration: [Migrating off package rollback](docs/upgrading/rollback-migration.md).
|
|
214
235
|
|
|
215
236
|
- **Removed: `Packages.rollback`** and the wire method `packages.rollback`. The player already answers the method with `unsupported` ("unknown package method"). To go back to an older version of a third-party plugin, the user uninstalls it and installs that version's `.vxp`.
|
|
216
237
|
- **Removed: `PluginInfo.previousVersion`** and the wire field `PackageInfo.previousVersion`; `packages.list` and `packages.changed` no longer carry it.
|
|
@@ -221,7 +242,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
221
242
|
|
|
222
243
|
## 1.5.16
|
|
223
244
|
|
|
224
|
-
**Package rollback is deprecated: the player keeps no previous plugin version.** `apiVersion` stays `vestra/1`; code still compiles, and plugins that do not manage other plugins need no change. 1.5.17 removes the deprecated symbols, as 1.5.8 removed `PackageCandidate.trial()` at patch level. Migration: [Migrating off package rollback](docs/
|
|
245
|
+
**Package rollback is deprecated: the player keeps no previous plugin version.** `apiVersion` stays `vestra/1`; code still compiles, and plugins that do not manage other plugins need no change. 1.5.17 removes the deprecated symbols, as 1.5.8 removed `PackageCandidate.trial()` at patch level. Migration: [Migrating off package rollback](docs/upgrading/rollback-migration.md).
|
|
225
246
|
|
|
226
247
|
- **Deprecated: `Packages.rollback`** (removed in 1.5.17). The player no longer has `packages.rollback` and answers it with `unsupported` ("unknown package method"); the SDK still forwards the call, so the error reaches the caller. To go back to an older version of a third-party plugin, the user uninstalls it and installs that version's `.vxp`.
|
|
227
248
|
- **Deprecated: `PluginInfo.previousVersion`** and the wire field `PackageInfo.previousVersion` (removed in 1.5.17): always `null`, since an upgrade replaces the package. The SDK reads a missing field as `null`.
|
|
@@ -299,7 +320,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
299
320
|
|
|
300
321
|
## 1.5.9
|
|
301
322
|
|
|
302
|
-
**Translations** (player 0.4.3). Additive; `apiVersion` stays `vestra/1`, and plugins written for 1.5.8 need no change. One test-visible change: a core action's `declaration.path` in `TestHost` is now the player's English key (read `display` for what the menu shows). The player now shows its interface in English, Simplified Chinese, Japanese, Korean, German, French or Spanish, chosen at start-up; a plugin writes every text in English and ships one gettext file per language. Plugins write no translation code: the player translates. Guides: [Translate a plugin](docs/how-to/i18n.md), [Migrate a plugin to English source text](docs/
|
|
323
|
+
**Translations** (player 0.4.3). Additive; `apiVersion` stays `vestra/1`, and plugins written for 1.5.8 need no change. One test-visible change: a core action's `declaration.path` in `TestHost` is now the player's English key (read `display` for what the menu shows). The player now shows its interface in English, Simplified Chinese, Japanese, Korean, German, French or Spanish, chosen at start-up; a plugin writes every text in English and ships one gettext file per language. Plugins write no translation code: the player translates. Guides: [Translate a plugin](docs/how-to/i18n.md), [Migrate a plugin to English source text](docs/upgrading/i18n-migration.md); example: `examples/i18n`.
|
|
303
324
|
|
|
304
325
|
- **`plugin.i18n.locale`** (new `I18n`, `Locale`): the interface language of this run, `"en"`, `"zh-Hans"`, `"ja"`, `"ko"`, `"de"`, `"fr"` or `"es"`; it never changes while the player runs. `"en"` from older players.
|
|
305
326
|
- **Templates with arguments**: `osd.show(text, args?)`, `OsdPanel.set(text, args?)` and `ConfirmOptions.args` (new `TemplateArgs`: an array for `{}` / `{0}`, an object for `{name}`). The player translates the template with the plugin's catalog, then fills it in. Without `args` the text shows as it is, so existing calls are unchanged; older players show the template unfilled.
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ export default async function activate(plugin: Plugin) {
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Run `npx vestra-plugin pack` to check and package it, or start the player with `--plugin <folder>` to try it right away. This is [`examples/hello`](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/hello); [Getting started](https://vestrasys.github.io/vestra-player-sdk/documents/Getting_started.html)
|
|
60
|
+
Run `npx vestra-plugin pack` to check and package it, or start the player with `--plugin <folder>` to try it right away. This is [`examples/hello`](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples/hello); the [Tutorials](https://vestrasys.github.io/vestra-player-sdk/documents/Tutorials.html) build a plugin in four lessons: [Getting started](https://vestrasys.github.io/vestra-player-sdk/documents/Getting_started.html) (a menu item and an on-screen message), then a window, settings and translations, and tests, packing and publishing.
|
|
61
61
|
|
|
62
62
|
## Examples
|
|
63
63
|
|
|
@@ -79,12 +79,13 @@ Complete, tested plugins in [`examples/`](https://github.com/vestrasys/vestra-pl
|
|
|
79
79
|
|
|
80
80
|
The [documentation site](https://vestrasys.github.io/vestra-player-sdk/) is the one place for everything a plugin author needs:
|
|
81
81
|
|
|
82
|
-
- **[Tutorials](https://vestrasys.github.io/vestra-player-sdk/documents/Tutorials.html)**: [Getting started](https://vestrasys.github.io/vestra-player-sdk/documents/Getting_started.html),
|
|
83
|
-
- **[
|
|
84
|
-
- **[
|
|
85
|
-
- **[
|
|
82
|
+
- **[Tutorials](https://vestrasys.github.io/vestra-player-sdk/documents/Tutorials.html)**: four lessons that build one plugin, starting with [Getting started](https://vestrasys.github.io/vestra-player-sdk/documents/Getting_started.html): a menu item, a window, settings and translations, tests and the store; then a lesson on writing your own blur kernel (a GPU shader and a convolution node).
|
|
83
|
+
- **[How-to guides](https://vestrasys.github.io/vestra-player-sdk/documents/How-to_guides.html)**: one task each (goal, steps, complete example): playback, storage, subtitles, sliders, window state, the GPU switch, menu state that needs no running plugin (`plugin.when`), translating, testing, publishing; and troubleshooting.
|
|
84
|
+
- **[Reference](https://vestrasys.github.io/vestra-player-sdk/documents/Reference.html)**: capabilities, manifest fields, limits, menus and bindings, platforms, theme, error codes, translations, the test host, and the [command line](https://vestrasys.github.io/vestra-player-sdk/documents/Command_line.html) (`vestra-plugin check`, `test`, `build`, `pack`, `i18n extract`, `i18n check`).
|
|
85
|
+
- **[Explanation](https://vestrasys.github.io/vestra-player-sdk/documents/Explanation.html)**: how a plugin runs, commands and key bindings, playback, video and audio processing, the interface, GPU and software rendering, the player's Rust core and how it runs and talks to plugins.
|
|
86
|
+
- **[Guidelines](https://vestrasys.github.io/vestra-player-sdk/documents/Guidelines.html)**: rules for every plugin, the interface and the menus.
|
|
86
87
|
- **[Examples](https://vestrasys.github.io/vestra-player-sdk/documents/Examples.html)**: the walkthroughs above.
|
|
87
|
-
- **[
|
|
88
|
+
- **[Upgrading](https://vestrasys.github.io/vestra-player-sdk/documents/Upgrading.html)**: what to change when a release deprecates or removes something, with the migration checklists and every release's "who must change".
|
|
88
89
|
- **[API](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html)**: every type and member of `@vestra-player/sdk` and `@vestra-player/sdk/testing`.
|
|
89
90
|
|
|
90
91
|
## Compatibility
|
package/dist/api-requests.js
CHANGED
|
@@ -146,4 +146,5 @@ export const API_REQUESTS = Object.freeze({
|
|
|
146
146
|
"Rendering.restartPlayer": [{ method: "rendering.restart" }],
|
|
147
147
|
"VideoUpscaling.state": [{ method: "video.upscaling.state" }],
|
|
148
148
|
"VideoUpscaling.set": [{ method: "video.upscaling.set" }],
|
|
149
|
+
"VideoUpscaling.onChange": [listen("video.upscaling.changed")],
|
|
149
150
|
});
|
|
@@ -85,6 +85,7 @@ export const HOST_CAPABILITIES = {
|
|
|
85
85
|
cases: {
|
|
86
86
|
"bindings.changed": "bindings.manage",
|
|
87
87
|
"packages.changed": null,
|
|
88
|
+
"video.upscaling.changed": "video.upscaling",
|
|
88
89
|
},
|
|
89
90
|
otherwise: "events",
|
|
90
91
|
param: "event",
|
|
@@ -96,6 +97,7 @@ export const HOST_CAPABILITIES = {
|
|
|
96
97
|
cases: {
|
|
97
98
|
"bindings.changed": "bindings.manage",
|
|
98
99
|
"packages.changed": null,
|
|
100
|
+
"video.upscaling.changed": "video.upscaling",
|
|
99
101
|
},
|
|
100
102
|
otherwise: "events",
|
|
101
103
|
param: "event",
|
package/dist/plugin/services.js
CHANGED
|
@@ -508,16 +508,38 @@ export const videoUpscaling = (scope) => {
|
|
|
508
508
|
if (!scope.runtime.can("video.upscaling"))
|
|
509
509
|
throw failure("capability", "video.upscaling must be declared in manifest");
|
|
510
510
|
};
|
|
511
|
+
// This plugin's own request, kept from every answer for `onChange`: the
|
|
512
|
+
// event leaves it out (it is each plugin's own), and only this plugin's
|
|
513
|
+
// `set` changes it.
|
|
514
|
+
const noted = (state) => {
|
|
515
|
+
enabledOf.set(scope.runtime, state.enabled);
|
|
516
|
+
return state;
|
|
517
|
+
};
|
|
511
518
|
return {
|
|
512
519
|
async state() {
|
|
513
520
|
declared();
|
|
514
|
-
return (await scope.runtime.host.video.upscalingState());
|
|
521
|
+
return noted(await scope.runtime.host.video.upscalingState());
|
|
515
522
|
},
|
|
516
523
|
async set(enabled) {
|
|
517
524
|
declared();
|
|
518
525
|
if (typeof enabled !== "boolean")
|
|
519
526
|
throw failure("invalid", "enabled must be true or false");
|
|
520
|
-
return (await scope.runtime.host.video.upscalingSet(enabled));
|
|
527
|
+
return noted(await scope.runtime.host.video.upscalingSet(enabled));
|
|
528
|
+
},
|
|
529
|
+
onChange(listener) {
|
|
530
|
+
declared();
|
|
531
|
+
const watch = scope.runtime.on("video.upscaling.changed", (data) => scope.run(listener, {
|
|
532
|
+
...data,
|
|
533
|
+
enabled: enabledOf.get(scope.runtime) ?? false,
|
|
534
|
+
}), "video.upscaling");
|
|
535
|
+
return scope.add({
|
|
536
|
+
dispose: async () => {
|
|
537
|
+
scope.forget(watch);
|
|
538
|
+
await watch.dispose();
|
|
539
|
+
},
|
|
540
|
+
});
|
|
521
541
|
},
|
|
522
542
|
};
|
|
523
543
|
};
|
|
544
|
+
/** Each plugin's own upscaling request, as its last answer said. */
|
|
545
|
+
const enabledOf = new WeakMap();
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { CommandDeclaration } from "../commands.ts";
|
|
|
16
16
|
import type { SourceState } from "../source.ts";
|
|
17
17
|
import type { SubtitleState } from "../subtitles.ts";
|
|
18
18
|
import { VIDEO_PROCESSING_LIMITS } from "../processing.ts";
|
|
19
|
-
import type { RenderingMode, RenderingPreference, RenderingReason, RenderSettings, UpscalingReason, ViewSettings } from "./types.ts";
|
|
19
|
+
import type { RenderingMode, RenderingPreference, RenderingReason, RenderSettings, UpscalingDecodingCause, UpscalingPlugin, UpscalingReason, UpscalingVideoSize, ViewSettings } from "./types.ts";
|
|
20
20
|
import type { MediaJob } from "../media.ts";
|
|
21
21
|
import type { LanguagePreference, Locale, Plugin, TemplateArgs, TextValue, ThemeId } from "./types.ts";
|
|
22
22
|
import { LOCALES, LIST_SEPARATORS, TEXT_LIMITS } from "../i18n.ts";
|
|
@@ -1862,7 +1862,71 @@ export declare class TestHost implements Transport {
|
|
|
1862
1862
|
checked: boolean;
|
|
1863
1863
|
/** GPU rendering is still starting: a `rendering` reason then lets `set(true)` keep the request. */
|
|
1864
1864
|
renderingStarting: boolean;
|
|
1865
|
+
/** The video's size the state reports (player 0.5.25); `null`: no video. */
|
|
1866
|
+
video: UpscalingVideoSize | null;
|
|
1867
|
+
/** The video area the state reports (player 0.5.25); `null`: none. */
|
|
1868
|
+
area: {
|
|
1869
|
+
/** Width in physical pixels. */
|
|
1870
|
+
width: number;
|
|
1871
|
+
/** Height in physical pixels. */
|
|
1872
|
+
height: number;
|
|
1873
|
+
} | null;
|
|
1874
|
+
/** The rendering GPU's name the state reports (player 0.5.25). */
|
|
1875
|
+
gpu: string | null;
|
|
1876
|
+
/** mpv's decoder the state reports (player 0.5.25); `null`: no video. */
|
|
1877
|
+
hwdecCurrent: string | null;
|
|
1865
1878
|
};
|
|
1879
|
+
/**
|
|
1880
|
+
* Make the upscaler's reason `decoding` with `cause`, with the player's
|
|
1881
|
+
* message (player 0.5.25), and set `upscaling.hwdecCurrent` to match
|
|
1882
|
+
* (`d3d11va-copy`, `no`, `nvdec`). `plugins` are named for `copy-back`
|
|
1883
|
+
* only, as the player does. Returns the reason; call
|
|
1884
|
+
* {@link TestHost.upscalingChanged} to tell a listening plugin.
|
|
1885
|
+
*
|
|
1886
|
+
* @example Test the window's advice for each cause
|
|
1887
|
+
* The plugin's window explains why upscaling waits. The test fakes the
|
|
1888
|
+
* copy-back cause that Picture Adjustments' filters cause and checks
|
|
1889
|
+
* that the window names the plugin.
|
|
1890
|
+
* ```ts
|
|
1891
|
+
* import assert from "node:assert/strict";
|
|
1892
|
+
* declare const host: TestHost;
|
|
1893
|
+
* // ---cut---
|
|
1894
|
+
* host.upscalingDecoding("copy-back", [{ id: "vestra.video", name: "Picture Adjustments" }]);
|
|
1895
|
+
* host.upscalingChanged();
|
|
1896
|
+
* await host.settle();
|
|
1897
|
+
* assert.match(String(host.window("upscale").props.blocker), /Picture Adjustments/);
|
|
1898
|
+
* ```
|
|
1899
|
+
*
|
|
1900
|
+
* @capability None.
|
|
1901
|
+
* @since 1.5.43
|
|
1902
|
+
*/
|
|
1903
|
+
upscalingDecoding(cause: UpscalingDecodingCause, plugins?: readonly UpscalingPlugin[]): UpscalingReason;
|
|
1904
|
+
/**
|
|
1905
|
+
* Send `video.upscaling.changed` with the state {@link TestHost.upscaling}
|
|
1906
|
+
* describes now (without `enabled`), as the player does when it changes
|
|
1907
|
+
* (player 0.5.25); a plugin listening with
|
|
1908
|
+
* `plugin.video.upscaling.onChange` receives it. Nothing reaches a plugin
|
|
1909
|
+
* that does not listen.
|
|
1910
|
+
*
|
|
1911
|
+
* @capability None.
|
|
1912
|
+
*
|
|
1913
|
+
* @example Show the new factor in the plugin's window
|
|
1914
|
+
* The window resized: the test changes the area and the factor, sends
|
|
1915
|
+
* the change, and checks that the plugin's window shows the new factor.
|
|
1916
|
+
* ```ts
|
|
1917
|
+
* import assert from "node:assert/strict";
|
|
1918
|
+
* declare const host: TestHost;
|
|
1919
|
+
* // ---cut---
|
|
1920
|
+
* host.upscaling.area = { width: 2560, height: 1440 };
|
|
1921
|
+
* host.upscaling.factor = 1.33; // 1080p in a 1440p area
|
|
1922
|
+
* host.upscalingChanged(); // as the player after the resize settled
|
|
1923
|
+
* await host.settle();
|
|
1924
|
+
* assert.equal(host.window("upscale").props.factor, "×1.33");
|
|
1925
|
+
* ```
|
|
1926
|
+
*
|
|
1927
|
+
* @since 1.5.43
|
|
1928
|
+
*/
|
|
1929
|
+
upscalingChanged(): void;
|
|
1866
1930
|
/** What `player.chapters()` lists while media is ready. */
|
|
1867
1931
|
chapters: {
|
|
1868
1932
|
/** The chapter title, `null` without one. */
|
package/dist/plugin/testing.js
CHANGED
|
@@ -1837,7 +1837,104 @@ export class TestHost {
|
|
|
1837
1837
|
factor: 2,
|
|
1838
1838
|
checked: false,
|
|
1839
1839
|
renderingStarting: false,
|
|
1840
|
+
video: {
|
|
1841
|
+
width: 1920,
|
|
1842
|
+
height: 1080,
|
|
1843
|
+
displayWidth: 1920,
|
|
1844
|
+
displayHeight: 1080,
|
|
1845
|
+
},
|
|
1846
|
+
area: { width: 3840, height: 2160 },
|
|
1847
|
+
gpu: "NVIDIA GeForce RTX 4070 Laptop GPU",
|
|
1848
|
+
hwdecCurrent: "d3d11va",
|
|
1840
1849
|
};
|
|
1850
|
+
/**
|
|
1851
|
+
* Make the upscaler's reason `decoding` with `cause`, with the player's
|
|
1852
|
+
* message (player 0.5.25), and set `upscaling.hwdecCurrent` to match
|
|
1853
|
+
* (`d3d11va-copy`, `no`, `nvdec`). `plugins` are named for `copy-back`
|
|
1854
|
+
* only, as the player does. Returns the reason; call
|
|
1855
|
+
* {@link TestHost.upscalingChanged} to tell a listening plugin.
|
|
1856
|
+
*
|
|
1857
|
+
* @example Test the window's advice for each cause
|
|
1858
|
+
* The plugin's window explains why upscaling waits. The test fakes the
|
|
1859
|
+
* copy-back cause that Picture Adjustments' filters cause and checks
|
|
1860
|
+
* that the window names the plugin.
|
|
1861
|
+
* ```ts
|
|
1862
|
+
* import assert from "node:assert/strict";
|
|
1863
|
+
* declare const host: TestHost;
|
|
1864
|
+
* // ---cut---
|
|
1865
|
+
* host.upscalingDecoding("copy-back", [{ id: "vestra.video", name: "Picture Adjustments" }]);
|
|
1866
|
+
* host.upscalingChanged();
|
|
1867
|
+
* await host.settle();
|
|
1868
|
+
* assert.match(String(host.window("upscale").props.blocker), /Picture Adjustments/);
|
|
1869
|
+
* ```
|
|
1870
|
+
*
|
|
1871
|
+
* @capability None.
|
|
1872
|
+
* @since 1.5.43
|
|
1873
|
+
*/
|
|
1874
|
+
upscalingDecoding(cause, plugins = []) {
|
|
1875
|
+
const needs = "upscaling needs it decoded on the GPU that renders it (d3d11va)";
|
|
1876
|
+
const names = plugins.map((p) => p.name);
|
|
1877
|
+
const because = names.length === 0
|
|
1878
|
+
? "for example because a video filter needs the frames in memory"
|
|
1879
|
+
: names.length === 1
|
|
1880
|
+
? `because the video filter of ${names[0]} needs the frames in memory`
|
|
1881
|
+
: `because the video filters of ${names.join(", ")} need the frames in memory`;
|
|
1882
|
+
const [message, current] = cause === "copy-back"
|
|
1883
|
+
? [
|
|
1884
|
+
`the video is decoded with copy-back (auto-copy-safe), ${because}; ${needs}`,
|
|
1885
|
+
"d3d11va-copy",
|
|
1886
|
+
]
|
|
1887
|
+
: cause === "hwdec-off"
|
|
1888
|
+
? [
|
|
1889
|
+
"hardware decoding is off; upscaling needs the video decoded on the GPU that renders it (d3d11va)",
|
|
1890
|
+
"no",
|
|
1891
|
+
]
|
|
1892
|
+
: cause === "software"
|
|
1893
|
+
? [`this video is decoded in software; ${needs}`, "no"]
|
|
1894
|
+
: [`this video is decoded with nvdec; ${needs}`, "nvdec"];
|
|
1895
|
+
const reason = cause === "copy-back"
|
|
1896
|
+
? {
|
|
1897
|
+
code: "decoding",
|
|
1898
|
+
message,
|
|
1899
|
+
cause,
|
|
1900
|
+
plugins: plugins.map(({ id, name }) => ({ id, name })),
|
|
1901
|
+
}
|
|
1902
|
+
: { code: "decoding", message, cause };
|
|
1903
|
+
this.upscaling.reason = reason;
|
|
1904
|
+
this.upscaling.hwdecCurrent = current;
|
|
1905
|
+
return reason;
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Send `video.upscaling.changed` with the state {@link TestHost.upscaling}
|
|
1909
|
+
* describes now (without `enabled`), as the player does when it changes
|
|
1910
|
+
* (player 0.5.25); a plugin listening with
|
|
1911
|
+
* `plugin.video.upscaling.onChange` receives it. Nothing reaches a plugin
|
|
1912
|
+
* that does not listen.
|
|
1913
|
+
*
|
|
1914
|
+
* @capability None.
|
|
1915
|
+
*
|
|
1916
|
+
* @example Show the new factor in the plugin's window
|
|
1917
|
+
* The window resized: the test changes the area and the factor, sends
|
|
1918
|
+
* the change, and checks that the plugin's window shows the new factor.
|
|
1919
|
+
* ```ts
|
|
1920
|
+
* import assert from "node:assert/strict";
|
|
1921
|
+
* declare const host: TestHost;
|
|
1922
|
+
* // ---cut---
|
|
1923
|
+
* host.upscaling.area = { width: 2560, height: 1440 };
|
|
1924
|
+
* host.upscaling.factor = 1.33; // 1080p in a 1440p area
|
|
1925
|
+
* host.upscalingChanged(); // as the player after the resize settled
|
|
1926
|
+
* await host.settle();
|
|
1927
|
+
* assert.equal(host.window("upscale").props.factor, "×1.33");
|
|
1928
|
+
* ```
|
|
1929
|
+
*
|
|
1930
|
+
* @since 1.5.43
|
|
1931
|
+
*/
|
|
1932
|
+
upscalingChanged() {
|
|
1933
|
+
if (!this.subscriptions.has("video.upscaling.changed"))
|
|
1934
|
+
return;
|
|
1935
|
+
const { enabled: _, ...state } = this.#upscalingState(false);
|
|
1936
|
+
this.emit("video.upscaling.changed", state);
|
|
1937
|
+
}
|
|
1841
1938
|
/** What `player.chapters()` lists while media is ready. */
|
|
1842
1939
|
chapters = [];
|
|
1843
1940
|
#overrides = new Map();
|
|
@@ -3481,12 +3578,20 @@ export class TestHost {
|
|
|
3481
3578
|
message: "the player has not checked the GPU yet; state() or set(true) checks it",
|
|
3482
3579
|
};
|
|
3483
3580
|
const requested = enabled && reason === null && factor !== null && factor > 1;
|
|
3581
|
+
const { video, area, gpu, hwdecCurrent } = this.upscaling;
|
|
3484
3582
|
return {
|
|
3485
3583
|
available: reason === null,
|
|
3486
|
-
reason: reason
|
|
3584
|
+
reason: reason
|
|
3585
|
+
? structuredClone(reason)
|
|
3586
|
+
: null,
|
|
3487
3587
|
enabled,
|
|
3488
3588
|
requested,
|
|
3489
3589
|
factor: requested ? factor : null,
|
|
3590
|
+
video: video ? { ...video } : null,
|
|
3591
|
+
area: area ? { ...area } : null,
|
|
3592
|
+
gpu,
|
|
3593
|
+
hwdecCurrent,
|
|
3594
|
+
inChain: requested,
|
|
3490
3595
|
};
|
|
3491
3596
|
}
|
|
3492
3597
|
/** `rendering.state` from {@link TestHost.rendering} and `videoRender.gpu`. */
|