@kheleshikhi/studio-agent 0.6.1 → 0.9.0
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/README.md +269 -13
- package/dist/attempt.d.ts +4 -0
- package/dist/attempt.js +11 -1
- package/dist/attempt.js.map +1 -1
- package/dist/browser.d.ts +89 -3
- package/dist/browser.js +536 -14
- package/dist/browser.js.map +1 -1
- package/dist/cli.d.ts +20 -0
- package/dist/cli.js +851 -73
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +66 -0
- package/dist/context.js +207 -0
- package/dist/context.js.map +1 -0
- package/dist/daemon.js +109 -7
- package/dist/daemon.js.map +1 -1
- package/dist/episode-runner.d.ts +109 -0
- package/dist/episode-runner.js +381 -0
- package/dist/episode-runner.js.map +1 -0
- package/dist/episode.d.ts +332 -0
- package/dist/episode.js +648 -0
- package/dist/episode.js.map +1 -0
- package/dist/presence.d.ts +49 -0
- package/dist/presence.js +141 -0
- package/dist/presence.js.map +1 -0
- package/dist/project.d.ts +30 -0
- package/dist/project.js +204 -0
- package/dist/project.js.map +1 -0
- package/dist/render-jobs.d.ts +101 -0
- package/dist/render-jobs.js +241 -0
- package/dist/render-jobs.js.map +1 -0
- package/dist/scenario.d.ts +33 -1
- package/dist/scenario.js +143 -5
- package/dist/scenario.js.map +1 -1
- package/dist/shot-provenance.d.ts +66 -0
- package/dist/shot-provenance.js +114 -0
- package/dist/shot-provenance.js.map +1 -0
- package/dist/state.js +10 -6
- package/dist/state.js.map +1 -1
- package/dist/video.d.ts +197 -0
- package/dist/video.js +446 -0
- package/dist/video.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type Api = <T>(method: string, route: string, body?: unknown) => Promise<T>;
|
|
2
|
+
export declare const DEPENDENCY_SCHEMA = "kheleshikhi.scene-dependencies.v1";
|
|
3
|
+
export type DependencyRecord = {
|
|
4
|
+
schema: string;
|
|
5
|
+
compilerVersion?: string;
|
|
6
|
+
componentHeader?: string;
|
|
7
|
+
components?: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
definitionHash: string | null;
|
|
10
|
+
}>;
|
|
11
|
+
behaviors?: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
definitionHash: string | null;
|
|
14
|
+
}>;
|
|
15
|
+
audio?: Array<{
|
|
16
|
+
id: string;
|
|
17
|
+
sha256: string | null;
|
|
18
|
+
}>;
|
|
19
|
+
digest: string;
|
|
20
|
+
};
|
|
21
|
+
export type SceneIdentity = {
|
|
22
|
+
/** Experience Scene id (manifest id) or "main" for a Studio Scene shot. */
|
|
23
|
+
scene: string;
|
|
24
|
+
kind: "studio_source" | "approved_revision" | "gallery_package";
|
|
25
|
+
asset?: string | null;
|
|
26
|
+
revision?: string | null;
|
|
27
|
+
source_hash?: string | null;
|
|
28
|
+
manifest_path?: string | null;
|
|
29
|
+
package?: string | null;
|
|
30
|
+
package_sha256?: string | null;
|
|
31
|
+
dependencies_digest: string | null;
|
|
32
|
+
dependencies?: DependencyRecord | null;
|
|
33
|
+
note?: string;
|
|
34
|
+
};
|
|
35
|
+
export type ExperienceProvenance = {
|
|
36
|
+
schema: "kheleshikhi.experience-scene-provenance.v1";
|
|
37
|
+
preview_source_hash: string | null;
|
|
38
|
+
scenes: Record<string, SceneIdentity>;
|
|
39
|
+
/** Audio stable id -> sha256 of its exact bytes. */
|
|
40
|
+
audio: Record<string, string>;
|
|
41
|
+
digest: string;
|
|
42
|
+
};
|
|
43
|
+
export declare function canonicalJson(value: unknown): string;
|
|
44
|
+
export declare function digestOf(value: unknown): string;
|
|
45
|
+
/** A dependency record from a compile response, or null when it has none. */
|
|
46
|
+
export declare function dependencyRecord(compileResult: unknown): DependencyRecord | null;
|
|
47
|
+
/** Compile a Studio source and return its dependency record. */
|
|
48
|
+
export declare function compileDependencies(api: Api, asset: string, source: unknown): Promise<{
|
|
49
|
+
compiled: boolean;
|
|
50
|
+
source_hash: string | null;
|
|
51
|
+
dependencies: DependencyRecord | null;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* One approved Scene revision's identity: the asset's approved source (when
|
|
55
|
+
* it is the asset's current source, i.e. no newer draft hides it) compiled
|
|
56
|
+
* for its dependency record. A draft over the revision leaves the record
|
|
57
|
+
* unknown, which is said, never guessed.
|
|
58
|
+
*/
|
|
59
|
+
export declare function approvedSceneIdentity(api: Api, scene: string, asset: string, revision: string, manifestPath: string | null): Promise<SceneIdentity>;
|
|
60
|
+
/**
|
|
61
|
+
* Every Scene and sound an Experience document resolves to right now, from
|
|
62
|
+
* the server's draft preview (the same resolution the player uses).
|
|
63
|
+
*/
|
|
64
|
+
export declare function experienceProvenance(api: Api, document: unknown, options?: {
|
|
65
|
+
dependencies?: boolean;
|
|
66
|
+
}): Promise<ExperienceProvenance>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a render was made from, exactly enough to key a cache on.
|
|
3
|
+
*
|
|
4
|
+
* A Scene render is made from one Scene Source (its source hash) compiled
|
|
5
|
+
* against exact Component, Behavior and Audio definitions: the server's
|
|
6
|
+
* compile response carries that record (`artifacts.dependencies`, schema
|
|
7
|
+
* `kheleshikhi.scene-dependencies.v1`, with its own digest). An Experience
|
|
8
|
+
* render is made from the Experience Source and, for each of its Scenes, the
|
|
9
|
+
* approved revision the draft preview resolved (asset, revision, stored
|
|
10
|
+
* manifest) plus that revision's dependency record when it can be read, and
|
|
11
|
+
* from the exact bytes of every sound it names.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here decides validity on its own; the episode runner hashes these
|
|
14
|
+
* facts into a shot key. Library or project membership is never an input.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from "node:crypto";
|
|
17
|
+
export const DEPENDENCY_SCHEMA = "kheleshikhi.scene-dependencies.v1";
|
|
18
|
+
export function canonicalJson(value) {
|
|
19
|
+
if (value === null || typeof value !== "object")
|
|
20
|
+
return JSON.stringify(value);
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
23
|
+
const record = value;
|
|
24
|
+
return `{${Object.keys(record).filter((key) => record[key] !== undefined).sort()
|
|
25
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
26
|
+
}
|
|
27
|
+
export function digestOf(value) {
|
|
28
|
+
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
29
|
+
}
|
|
30
|
+
/** A dependency record from a compile response, or null when it has none. */
|
|
31
|
+
export function dependencyRecord(compileResult) {
|
|
32
|
+
const artifacts = compileResult?.artifacts;
|
|
33
|
+
const record = artifacts?.dependencies;
|
|
34
|
+
if (!record || record.schema !== DEPENDENCY_SCHEMA || typeof record.digest !== "string")
|
|
35
|
+
return null;
|
|
36
|
+
return record;
|
|
37
|
+
}
|
|
38
|
+
/** Compile a Studio source and return its dependency record. */
|
|
39
|
+
export async function compileDependencies(api, asset, source) {
|
|
40
|
+
const result = await api("POST", `/v1/agent/studio/${encodeURIComponent(asset)}/compile`, { source });
|
|
41
|
+
return {
|
|
42
|
+
compiled: result.compiled === true,
|
|
43
|
+
source_hash: typeof result.source_hash === "string" ? result.source_hash : null,
|
|
44
|
+
dependencies: dependencyRecord(result),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One approved Scene revision's identity: the asset's approved source (when
|
|
49
|
+
* it is the asset's current source, i.e. no newer draft hides it) compiled
|
|
50
|
+
* for its dependency record. A draft over the revision leaves the record
|
|
51
|
+
* unknown, which is said, never guessed.
|
|
52
|
+
*/
|
|
53
|
+
export async function approvedSceneIdentity(api, scene, asset, revision, manifestPath) {
|
|
54
|
+
const identity = { scene, kind: "approved_revision", asset, revision, manifest_path: manifestPath, dependencies_digest: null };
|
|
55
|
+
try {
|
|
56
|
+
const loaded = await api("GET", `/v1/agent/studio/${encodeURIComponent(asset)}`);
|
|
57
|
+
if (loaded.source_origin === "approved" && loaded.base_revision_public_id === revision && loaded.source) {
|
|
58
|
+
const compiled = await compileDependencies(api, asset, loaded.source);
|
|
59
|
+
identity.source_hash = typeof loaded.source_hash === "string" ? loaded.source_hash : compiled.source_hash;
|
|
60
|
+
identity.dependencies = compiled.dependencies;
|
|
61
|
+
identity.dependencies_digest = compiled.dependencies?.digest ?? null;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
identity.note = "A newer draft or revision hides this revision's source; keyed by revision and stored manifest only.";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
identity.note = `Source not readable: ${error instanceof Error ? error.message : String(error)}`.slice(0, 300);
|
|
69
|
+
}
|
|
70
|
+
return identity;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Every Scene and sound an Experience document resolves to right now, from
|
|
74
|
+
* the server's draft preview (the same resolution the player uses).
|
|
75
|
+
*/
|
|
76
|
+
export async function experienceProvenance(api, document, options = {}) {
|
|
77
|
+
const preview = await api("POST", "/v1/agent/authoring/experience/preview", { document });
|
|
78
|
+
const scenes = {};
|
|
79
|
+
for (const [scene, raw] of Object.entries((preview.scenes ?? {}))) {
|
|
80
|
+
if (raw.kind === "media") {
|
|
81
|
+
const origin = (raw.provenance ?? {});
|
|
82
|
+
const asset = typeof origin.asset === "string" ? origin.asset : null;
|
|
83
|
+
const revision = typeof origin.revision === "string" ? origin.revision : null;
|
|
84
|
+
const manifestPath = typeof raw.manifestPath === "string" ? raw.manifestPath : null;
|
|
85
|
+
if (asset && revision && options.dependencies !== false) {
|
|
86
|
+
scenes[scene] = await approvedSceneIdentity(api, scene, asset, revision, manifestPath);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
scenes[scene] = { scene, kind: "approved_revision", asset, revision, manifest_path: manifestPath,
|
|
90
|
+
source_hash: typeof origin.sourceHash === "string" ? origin.sourceHash : null, dependencies_digest: null };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
scenes[scene] = { scene, kind: "gallery_package", package: raw.package ?? null, package_sha256: raw.packageSha256 ?? null,
|
|
95
|
+
dependencies_digest: null, note: "An approved Gallery package: its sha256 is its identity." };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const audio = {};
|
|
99
|
+
for (const entry of Object.values((preview.audio ?? {}))) {
|
|
100
|
+
if (typeof entry.audio === "string" && typeof entry.sha256 === "string")
|
|
101
|
+
audio[entry.audio] = entry.sha256;
|
|
102
|
+
}
|
|
103
|
+
const body = { preview_source_hash: typeof preview.sourceHash === "string" ? preview.sourceHash : null, scenes, audio };
|
|
104
|
+
return {
|
|
105
|
+
schema: "kheleshikhi.experience-scene-provenance.v1",
|
|
106
|
+
...body,
|
|
107
|
+
digest: digestOf({
|
|
108
|
+
preview_source_hash: body.preview_source_hash,
|
|
109
|
+
scenes: Object.fromEntries(Object.entries(scenes).map(([key, value]) => [key, { ...value, dependencies: undefined, note: undefined }])),
|
|
110
|
+
audio,
|
|
111
|
+
}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=shot-provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shot-provenance.js","sourceRoot":"","sources":["../src/shot-provenance.ts"],"names":[],"mappings":"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"}
|
package/dist/state.js
CHANGED
|
@@ -5,25 +5,29 @@ import path from "node:path";
|
|
|
5
5
|
import { CliError } from "./types.js";
|
|
6
6
|
export const DEFAULT_API_ORIGIN = "https://api.nerdbro.org";
|
|
7
7
|
export function stateDirectory(env = process.env, platform = process.platform) {
|
|
8
|
+
// Paths follow the target platform's convention, wherever this runs.
|
|
9
|
+
const join = platform === "win32" ? path.win32.join : path.posix.join;
|
|
8
10
|
if (env.NERDBRO_AGENT_STATE_DIR)
|
|
9
11
|
return path.resolve(env.NERDBRO_AGENT_STATE_DIR);
|
|
10
12
|
if (platform === "win32") {
|
|
11
|
-
return
|
|
13
|
+
return join(env.LOCALAPPDATA || join(os.homedir(), "AppData", "Local"), "NerdBro", "Studio Agent");
|
|
12
14
|
}
|
|
13
15
|
if (platform === "darwin") {
|
|
14
|
-
return
|
|
16
|
+
return join(os.homedir(), "Library", "Application Support", "NerdBro", "Studio Agent");
|
|
15
17
|
}
|
|
16
|
-
return
|
|
18
|
+
return join(env.XDG_STATE_HOME || join(os.homedir(), ".local", "state"), "nerdbro-studio-agent");
|
|
17
19
|
}
|
|
18
20
|
export function cacheDirectory(env = process.env, platform = process.platform) {
|
|
21
|
+
// Paths follow the target platform's convention, wherever this runs.
|
|
22
|
+
const join = platform === "win32" ? path.win32.join : path.posix.join;
|
|
19
23
|
if (env.NERDBRO_AGENT_CACHE_DIR)
|
|
20
24
|
return path.resolve(env.NERDBRO_AGENT_CACHE_DIR);
|
|
21
25
|
if (platform === "win32") {
|
|
22
|
-
return
|
|
26
|
+
return join(env.LOCALAPPDATA || join(os.homedir(), "AppData", "Local"), "NerdBro", "Studio Agent", "Cache");
|
|
23
27
|
}
|
|
24
28
|
if (platform === "darwin")
|
|
25
|
-
return
|
|
26
|
-
return
|
|
29
|
+
return join(os.homedir(), "Library", "Caches", "NerdBro Studio Agent");
|
|
30
|
+
return join(env.XDG_CACHE_HOME || join(os.homedir(), ".cache"), "nerdbro-studio-agent");
|
|
27
31
|
}
|
|
28
32
|
function daemonPath() {
|
|
29
33
|
return path.join(stateDirectory(), "daemon.json");
|
package/dist/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE5D,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ;IAC9F,IAAI,GAAG,CAAC,uBAAuB;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAClF,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
package/dist/video.d.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export declare const RECIPE_KIND = "kheleshikhi.video-render-recipe.v1";
|
|
2
|
+
export declare const RENDER_KIND = "kheleshikhi.video-render.v1";
|
|
3
|
+
export type RenderSubject = "scene" | "experience";
|
|
4
|
+
/** Learner actions a recipe may schedule, per subject. */
|
|
5
|
+
export declare const RENDER_OPS: Record<RenderSubject, readonly string[]>;
|
|
6
|
+
export type RenderAction = {
|
|
7
|
+
/** Video second at which the action happens. */
|
|
8
|
+
at: number;
|
|
9
|
+
op: string;
|
|
10
|
+
target?: string;
|
|
11
|
+
value?: string | number | boolean;
|
|
12
|
+
};
|
|
13
|
+
/** The native output sizes: the presentation host renders at exactly these. */
|
|
14
|
+
export declare const RENDER_PROFILES: {
|
|
15
|
+
readonly "landscape-720p": {
|
|
16
|
+
readonly width: 1280;
|
|
17
|
+
readonly height: 720;
|
|
18
|
+
};
|
|
19
|
+
readonly "landscape-1080p": {
|
|
20
|
+
readonly width: 1920;
|
|
21
|
+
readonly height: 1080;
|
|
22
|
+
};
|
|
23
|
+
readonly "portrait-720p": {
|
|
24
|
+
readonly width: 720;
|
|
25
|
+
readonly height: 1280;
|
|
26
|
+
};
|
|
27
|
+
readonly "portrait-1080p": {
|
|
28
|
+
readonly width: 1080;
|
|
29
|
+
readonly height: 1920;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export type RenderProfile = keyof typeof RENDER_PROFILES;
|
|
33
|
+
export type RenderRecipe = {
|
|
34
|
+
kind: typeof RECIPE_KIND;
|
|
35
|
+
/** Output frames per second. */
|
|
36
|
+
fps: number;
|
|
37
|
+
/** SceneClock seconds rendered: [start, end); frame k at start + k / fps. */
|
|
38
|
+
start: number;
|
|
39
|
+
end: number;
|
|
40
|
+
/** One of [RENDER_PROFILES]; its size is the output size. */
|
|
41
|
+
profile: RenderProfile;
|
|
42
|
+
/** Output size: the presentation viewport, every PNG and the MP4. */
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
/** `scene` mixes the runtime's own sounds from canonical assets; `silent` writes a silent AAC track. */
|
|
46
|
+
audio: "silent" | "scene";
|
|
47
|
+
/** Learner actions at exact logical times. */
|
|
48
|
+
actions: RenderAction[];
|
|
49
|
+
crf: number;
|
|
50
|
+
};
|
|
51
|
+
export declare const RECIPE_LIMITS: {
|
|
52
|
+
maxSeconds: number;
|
|
53
|
+
maxFps: number;
|
|
54
|
+
maxFrames: number;
|
|
55
|
+
maxActions: number;
|
|
56
|
+
};
|
|
57
|
+
/** Parse and bound a render recipe. Unknown fields are refused, never ignored. */
|
|
58
|
+
export declare function parseRecipe(value: unknown, subject?: RenderSubject): RenderRecipe;
|
|
59
|
+
/** Width and height of a PNG, from its IHDR chunk; refuses anything else. */
|
|
60
|
+
export declare function pngSize(data: Uint8Array): {
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
};
|
|
64
|
+
/** The exact logical second of frame `index`: computed, never accumulated. */
|
|
65
|
+
export declare function frameTime(recipe: RenderRecipe, index: number): number;
|
|
66
|
+
/**
|
|
67
|
+
* The frame grid. Frame k exists exactly when `start + k / fps < end`: the
|
|
68
|
+
* span is half open, so a frame at `end` is not rendered and every frame
|
|
69
|
+
* before it is. The count is the smallest n with `start + n / fps >= end`,
|
|
70
|
+
* i.e. `ceil((end - start) * fps)`, with a tolerance of a billionth of a
|
|
71
|
+
* frame so a decimal end that is exactly on the grid (16 s at 30 fps is
|
|
72
|
+
* 480.00000000000006 in binary) is not rounded up a frame.
|
|
73
|
+
*/
|
|
74
|
+
export declare function frameCountFor(start: number, end: number, fps: number): number;
|
|
75
|
+
export declare function frameCount(recipe: RenderRecipe): number;
|
|
76
|
+
/** Learner actions due after `previous` and at or before `time`. */
|
|
77
|
+
export declare function actionsBetween(recipe: RenderRecipe, previous: number, time: number): RenderAction[];
|
|
78
|
+
export declare function sha256(data: string | Uint8Array): string;
|
|
79
|
+
export type Encoder = {
|
|
80
|
+
ffmpeg: string;
|
|
81
|
+
ffprobe: string;
|
|
82
|
+
version: string;
|
|
83
|
+
libx264: boolean;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* FFmpeg is an explicit, local dependency: `NERDBRO_FFMPEG` (and
|
|
87
|
+
* `NERDBRO_FFPROBE`) or `ffmpeg`/`ffprobe` on PATH. It is never downloaded.
|
|
88
|
+
*/
|
|
89
|
+
export declare function findEncoder(environment?: NodeJS.ProcessEnv): Promise<Encoder>;
|
|
90
|
+
/** Audio is mixed and encoded at 48 kHz stereo. */
|
|
91
|
+
export declare const MIX_SAMPLE_RATE = 48000;
|
|
92
|
+
/** The exact audio length of `frames` video frames, in 48 kHz samples. */
|
|
93
|
+
export declare function sampleCount(frames: number, fps: number): number;
|
|
94
|
+
/**
|
|
95
|
+
* The FFmpeg arguments for one recipe: H.264 High, yuv420p, CFR, AAC audio,
|
|
96
|
+
* faststart. Frames are already the output size (proved per PNG), so the
|
|
97
|
+
* picture is never scaled. `audio` is the mixed track, or none for silence.
|
|
98
|
+
*/
|
|
99
|
+
export declare function encodeArguments(recipe: RenderRecipe, frames: string, output: string, audio?: string): string[];
|
|
100
|
+
export declare function encode(encoder: Encoder, recipe: RenderRecipe, frames: string, output: string, audio?: string): Promise<void>;
|
|
101
|
+
export type Probe = {
|
|
102
|
+
duration: number;
|
|
103
|
+
frames: number;
|
|
104
|
+
width: number;
|
|
105
|
+
height: number;
|
|
106
|
+
video_codec: string;
|
|
107
|
+
audio_codec: string | null;
|
|
108
|
+
pix_fmt: string;
|
|
109
|
+
fps: string;
|
|
110
|
+
};
|
|
111
|
+
export declare function probe(encoder: Encoder, file: string): Promise<Probe>;
|
|
112
|
+
/** Refuse an encode that does not match its recipe: frame count, size, codecs and duration. */
|
|
113
|
+
export declare function verifyProbe(recipe: RenderRecipe, result: Probe): void;
|
|
114
|
+
/** One play or stop the runtime logged during a render (never sounded). */
|
|
115
|
+
export type RenderAudioEvent = {
|
|
116
|
+
at_ms: number;
|
|
117
|
+
action: "play" | "stop" | string;
|
|
118
|
+
id: string;
|
|
119
|
+
/** Experience Scene key for a Scene's own sound; absent for an Experience sound. */
|
|
120
|
+
scene?: string;
|
|
121
|
+
/** Where in the asset playback starts (an audio clock resuming mid-file). */
|
|
122
|
+
offset_ms?: number;
|
|
123
|
+
loop?: boolean;
|
|
124
|
+
volume?: number;
|
|
125
|
+
role?: string;
|
|
126
|
+
sha256?: string | null;
|
|
127
|
+
};
|
|
128
|
+
/** One stretch of one sound in the mix. */
|
|
129
|
+
export type AudioSegment = {
|
|
130
|
+
key: string;
|
|
131
|
+
at_ms: number;
|
|
132
|
+
offset_ms: number;
|
|
133
|
+
length_ms: number;
|
|
134
|
+
loop: boolean;
|
|
135
|
+
volume: number;
|
|
136
|
+
};
|
|
137
|
+
export declare const MIX_LIMITS: {
|
|
138
|
+
maxSegments: number;
|
|
139
|
+
maxSources: number;
|
|
140
|
+
};
|
|
141
|
+
export declare function audioKey(event: {
|
|
142
|
+
id: string;
|
|
143
|
+
scene?: string;
|
|
144
|
+
}): string;
|
|
145
|
+
/**
|
|
146
|
+
* The logged plays and stops as segments of a video that shows video time
|
|
147
|
+
* [startMs, endMs): a play opens one; the next stop or play of the same
|
|
148
|
+
* sound (a restart) closes it, and so does the end. A sound already playing
|
|
149
|
+
* at `startMs` enters the video that far into its asset. Times in the
|
|
150
|
+
* result are relative to the video's first frame. Events are in log order,
|
|
151
|
+
* which is time order.
|
|
152
|
+
*/
|
|
153
|
+
export declare function audioSegments(events: RenderAudioEvent[], endMs: number, startMs?: number): AudioSegment[];
|
|
154
|
+
/**
|
|
155
|
+
* FFmpeg arguments that mix [segments] from their canonical files into one
|
|
156
|
+
* 48 kHz stereo PCM track of exactly `round(durationMs * 48)` samples. Each
|
|
157
|
+
* segment is its own input (a looping sound is an endlessly looped input),
|
|
158
|
+
* trimmed from its offset to its length, scaled by its volume and delayed
|
|
159
|
+
* to its start; the segments are summed without normalisation, so a sound
|
|
160
|
+
* is as loud as the runtime would play it. Returns null when nothing sounded.
|
|
161
|
+
*
|
|
162
|
+
* Everything is counted in samples, not timestamps: a looped AAC input
|
|
163
|
+
* leaves a 1,024-sample timestamp gap at every wrap (its priming is skipped
|
|
164
|
+
* again), so a timestamp trim came out one AAC frame short per loop. Each
|
|
165
|
+
* input is renumbered by decoded sample (`asetpts=N/SR/TB`), which makes a
|
|
166
|
+
* loop gapless at the asset's own decoded length, and the mix is padded and
|
|
167
|
+
* trimmed to the exact sample count.
|
|
168
|
+
*/
|
|
169
|
+
export declare function mixArguments(segments: AudioSegment[], files: Map<string, string>, durationMs: number, output: string): string[] | null;
|
|
170
|
+
export declare function mix(encoder: Encoder, segments: AudioSegment[], files: Map<string, string>, durationMs: number, output: string): Promise<string[] | null>;
|
|
171
|
+
/** Peak and mean level of a file's audio, in dBFS: proof the track is not silence. */
|
|
172
|
+
export declare function audioLevel(encoder: Encoder, file: string): Promise<{
|
|
173
|
+
mean_db: number | null;
|
|
174
|
+
max_db: number | null;
|
|
175
|
+
}>;
|
|
176
|
+
export type PhaseSummary = {
|
|
177
|
+
count: number;
|
|
178
|
+
total_ms: number;
|
|
179
|
+
per_frame_ms: number;
|
|
180
|
+
p50_ms: number;
|
|
181
|
+
p95_ms: number;
|
|
182
|
+
max_ms: number;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Where capture time goes, per phase, across a render's frames: the frame
|
|
186
|
+
* loop records each phase's wall time and render.json reports totals,
|
|
187
|
+
* per-frame means and percentiles. Diagnostic only; it never affects a frame.
|
|
188
|
+
*/
|
|
189
|
+
export declare class PhaseTimes {
|
|
190
|
+
private readonly samples;
|
|
191
|
+
/** Only the frame loop records; setup and audio collection do not. */
|
|
192
|
+
active: boolean;
|
|
193
|
+
add(phase: string, milliseconds: number): void;
|
|
194
|
+
/** Time `body` as `phase`. */
|
|
195
|
+
time<T>(phase: string, body: () => Promise<T>): Promise<T>;
|
|
196
|
+
summary(frames: number): Record<string, PhaseSummary>;
|
|
197
|
+
}
|