@nodetool-ai/runtime 0.8.8 → 0.8.9-beta.2

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.
Files changed (148) hide show
  1. package/dist/.nodetool-build-stamp +1 -1
  2. package/dist/context.d.ts +6 -2
  3. package/dist/context.d.ts.map +1 -1
  4. package/dist/context.js +13 -24
  5. package/dist/context.js.map +1 -1
  6. package/dist/host-binaries.d.ts +5 -5
  7. package/dist/host-binaries.js +5 -5
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/invocation-account.d.ts +1 -42
  13. package/dist/invocation-account.d.ts.map +1 -1
  14. package/dist/invocation-account.js +1 -59
  15. package/dist/invocation-account.js.map +1 -1
  16. package/dist/media/abort.d.ts +8 -0
  17. package/dist/media/abort.d.ts.map +1 -0
  18. package/dist/media/abort.js +18 -0
  19. package/dist/media/abort.js.map +1 -0
  20. package/dist/media/audio-encoder.d.ts +37 -0
  21. package/dist/media/audio-encoder.d.ts.map +1 -0
  22. package/dist/media/audio-encoder.js +118 -0
  23. package/dist/media/audio-encoder.js.map +1 -0
  24. package/dist/media/audio.d.ts +36 -0
  25. package/dist/media/audio.d.ts.map +1 -0
  26. package/dist/media/audio.js +84 -0
  27. package/dist/media/audio.js.map +1 -0
  28. package/dist/media/av.d.ts +51 -0
  29. package/dist/media/av.d.ts.map +1 -0
  30. package/dist/media/av.js +146 -0
  31. package/dist/media/av.js.map +1 -0
  32. package/dist/media/codecs.d.ts +9 -0
  33. package/dist/media/codecs.d.ts.map +1 -0
  34. package/dist/media/codecs.js +17 -0
  35. package/dist/media/codecs.js.map +1 -0
  36. package/dist/media/concat.d.ts +56 -0
  37. package/dist/media/concat.d.ts.map +1 -0
  38. package/dist/media/concat.js +319 -0
  39. package/dist/media/concat.js.map +1 -0
  40. package/dist/media/convert.d.ts +31 -0
  41. package/dist/media/convert.d.ts.map +1 -0
  42. package/dist/media/convert.js +114 -0
  43. package/dist/media/convert.js.map +1 -0
  44. package/dist/media/decode.d.ts +31 -0
  45. package/dist/media/decode.d.ts.map +1 -0
  46. package/dist/media/decode.js +85 -0
  47. package/dist/media/decode.js.map +1 -0
  48. package/dist/media/filter-graph.d.ts +53 -0
  49. package/dist/media/filter-graph.d.ts.map +1 -0
  50. package/dist/media/filter-graph.js +260 -0
  51. package/dist/media/filter-graph.js.map +1 -0
  52. package/dist/media/frames.d.ts +31 -0
  53. package/dist/media/frames.d.ts.map +1 -0
  54. package/dist/media/frames.js +112 -0
  55. package/dist/media/frames.js.map +1 -0
  56. package/dist/media/index.d.ts +26 -0
  57. package/dist/media/index.d.ts.map +1 -0
  58. package/dist/media/index.js +26 -0
  59. package/dist/media/index.js.map +1 -0
  60. package/dist/media/mux.d.ts +58 -0
  61. package/dist/media/mux.d.ts.map +1 -0
  62. package/dist/media/mux.js +234 -0
  63. package/dist/media/mux.js.map +1 -0
  64. package/dist/media/probe.d.ts +84 -0
  65. package/dist/media/probe.d.ts.map +1 -0
  66. package/dist/media/probe.js +133 -0
  67. package/dist/media/probe.js.map +1 -0
  68. package/dist/media/rgba.d.ts +78 -0
  69. package/dist/media/rgba.d.ts.map +1 -0
  70. package/dist/media/rgba.js +285 -0
  71. package/dist/media/rgba.js.map +1 -0
  72. package/dist/media/testing.d.ts +119 -0
  73. package/dist/media/testing.d.ts.map +1 -0
  74. package/dist/media/testing.js +343 -0
  75. package/dist/media/testing.js.map +1 -0
  76. package/dist/media/transcode.d.ts +17 -0
  77. package/dist/media/transcode.d.ts.map +1 -0
  78. package/dist/media/transcode.js +19 -0
  79. package/dist/media/transcode.js.map +1 -0
  80. package/dist/media/transform.d.ts +61 -0
  81. package/dist/media/transform.d.ts.map +1 -0
  82. package/dist/media/transform.js +371 -0
  83. package/dist/media/transform.js.map +1 -0
  84. package/dist/media/verify.d.ts +17 -0
  85. package/dist/media/verify.d.ts.map +1 -0
  86. package/dist/media/verify.js +65 -0
  87. package/dist/media/verify.js.map +1 -0
  88. package/dist/media-loader.d.ts +8 -0
  89. package/dist/media-loader.d.ts.map +1 -0
  90. package/dist/media-loader.js +10 -0
  91. package/dist/media-loader.js.map +1 -0
  92. package/dist/prefixed-storage-adapter.d.ts.map +1 -1
  93. package/dist/prefixed-storage-adapter.js +1 -1
  94. package/dist/prefixed-storage-adapter.js.map +1 -1
  95. package/dist/providers/anthropic-provider.d.ts.map +1 -1
  96. package/dist/providers/anthropic-provider.js +1 -17
  97. package/dist/providers/anthropic-provider.js.map +1 -1
  98. package/dist/providers/data-uri.d.ts +6 -0
  99. package/dist/providers/data-uri.d.ts.map +1 -0
  100. package/dist/providers/data-uri.js +19 -0
  101. package/dist/providers/data-uri.js.map +1 -0
  102. package/dist/providers/fake-provider.js +1 -1
  103. package/dist/providers/meta-transcription.d.ts +1 -5
  104. package/dist/providers/meta-transcription.d.ts.map +1 -1
  105. package/dist/providers/meta-transcription.js +9 -55
  106. package/dist/providers/meta-transcription.js.map +1 -1
  107. package/dist/providers/node-llama-cpp-trending.generated.js +132 -132
  108. package/dist/providers/node-llama-cpp-trending.generated.js.map +1 -1
  109. package/dist/providers/ollama-provider.d.ts.map +1 -1
  110. package/dist/providers/ollama-provider.js +1 -17
  111. package/dist/providers/ollama-provider.js.map +1 -1
  112. package/dist/providers/openai-provider.js +2 -2
  113. package/dist/providers/openai-provider.js.map +1 -1
  114. package/dist/providers/openrouter-provider.d.ts.map +1 -1
  115. package/dist/providers/openrouter-provider.js +1 -0
  116. package/dist/providers/openrouter-provider.js.map +1 -1
  117. package/dist/providers/safe-url.d.ts +1 -69
  118. package/dist/providers/safe-url.d.ts.map +1 -1
  119. package/dist/providers/safe-url.js +1 -284
  120. package/dist/providers/safe-url.js.map +1 -1
  121. package/dist/providers/topaz-provider.d.ts +1 -1
  122. package/dist/providers/topaz-provider.js +1 -1
  123. package/dist/providers/video-frame-fallback.d.ts +1 -1
  124. package/dist/providers/video-frame-fallback.js +4 -4
  125. package/dist/providers/video-frame-fallback.js.map +1 -1
  126. package/dist/providers/video-frames.d.ts +6 -16
  127. package/dist/providers/video-frames.d.ts.map +1 -1
  128. package/dist/providers/video-frames.js +48 -116
  129. package/dist/providers/video-frames.js.map +1 -1
  130. package/dist/storage-workspace.d.ts +1 -61
  131. package/dist/storage-workspace.d.ts.map +1 -1
  132. package/dist/storage-workspace.js +2 -430
  133. package/dist/storage-workspace.js.map +1 -1
  134. package/dist/testing.d.ts.map +1 -1
  135. package/dist/testing.js +57 -1
  136. package/dist/testing.js.map +1 -1
  137. package/dist/trim-video-window.d.ts.map +1 -1
  138. package/dist/trim-video-window.js +2 -32
  139. package/dist/trim-video-window.js.map +1 -1
  140. package/dist/workspace.d.ts +1 -118
  141. package/dist/workspace.d.ts.map +1 -1
  142. package/dist/workspace.js +1 -111
  143. package/dist/workspace.js.map +1 -1
  144. package/dist/yt-dlp-format.d.ts +22 -0
  145. package/dist/yt-dlp-format.d.ts.map +1 -0
  146. package/dist/yt-dlp-format.js +90 -0
  147. package/dist/yt-dlp-format.js.map +1 -0
  148. package/package.json +23 -6
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Container probes on Mediabunny: duration, picture size, frame rates,
3
+ * streams and packet timestamps, read without decoding anything.
4
+ */
5
+ import { ALL_FORMATS, BufferSource, EncodedPacketSink, FilePathSource, Input } from "mediabunny";
6
+ import { throwIfAborted } from "./abort.js";
7
+ function openInput(source) {
8
+ return new Input({
9
+ source: typeof source === "string" ? new FilePathSource(source) : new BufferSource(source),
10
+ formats: ALL_FORMATS
11
+ });
12
+ }
13
+ /**
14
+ * Read `source`'s streams and timing. Throws when the bytes are not a
15
+ * container Mediabunny recognizes; callers that treat unreadable media as
16
+ * "no audio" or "no duration" catch that.
17
+ */
18
+ export async function probeMedia(source, options = {}) {
19
+ const { signal } = options;
20
+ throwIfAborted(signal);
21
+ const input = openInput(source);
22
+ try {
23
+ const format = (await input.getFormat()).name;
24
+ throwIfAborted(signal);
25
+ const videoTrack = await input.getPrimaryVideoTrack();
26
+ const audioTrack = await input.getPrimaryAudioTrack();
27
+ const durationSec = (await input.getDurationFromMetadata()) ?? (await input.computeDuration());
28
+ throwIfAborted(signal);
29
+ let video = null;
30
+ if (videoTrack) {
31
+ const target = options.frameRatePackets ?? 256;
32
+ const rates = await videoTrack.computeFrameRateMetrics({
33
+ // Mediabunny refuses Infinity, though its docs offer it for a full scan.
34
+ targetPacketCount: Number.isFinite(target) ? target : Number.MAX_SAFE_INTEGER
35
+ });
36
+ throwIfAborted(signal);
37
+ video = {
38
+ codec: videoTrack.codec,
39
+ width: videoTrack.codedWidth,
40
+ height: videoTrack.codedHeight,
41
+ displayWidth: videoTrack.displayWidth,
42
+ displayHeight: videoTrack.displayHeight,
43
+ rotation: videoTrack.rotation,
44
+ nominalFps: rates.underlyingFrameRate,
45
+ averageFps: rates.averageFrameRate,
46
+ constantFrameRate: rates.frameRateIsConstant,
47
+ alpha: await videoTrack.canBeTransparent(),
48
+ timeResolution: videoTrack.timeResolution
49
+ };
50
+ }
51
+ const audio = audioTrack
52
+ ? {
53
+ codec: audioTrack.codec,
54
+ sampleRate: audioTrack.sampleRate,
55
+ channels: audioTrack.numberOfChannels
56
+ }
57
+ : null;
58
+ return {
59
+ format,
60
+ durationSec: Number.isFinite(durationSec) ? durationSec : null,
61
+ video,
62
+ audio
63
+ };
64
+ }
65
+ finally {
66
+ input.dispose();
67
+ }
68
+ }
69
+ /**
70
+ * {@link probeMedia}, or `null` when `source` is not media Mediabunny reads.
71
+ * An abort still throws.
72
+ */
73
+ async function probeOrNull(source, options) {
74
+ try {
75
+ return await probeMedia(source, options);
76
+ }
77
+ catch (error) {
78
+ if (options.signal?.aborted)
79
+ throw error;
80
+ return null;
81
+ }
82
+ }
83
+ /**
84
+ * `source`'s duration in seconds, or `null` when it is not media Mediabunny
85
+ * reads or names no duration. An abort still throws.
86
+ */
87
+ export async function probeDurationSec(source, options = {}) {
88
+ return (await probeOrNull(source, options))?.durationSec ?? null;
89
+ }
90
+ /**
91
+ * Whether `source` has an audio stream. Media Mediabunny cannot read has
92
+ * none. An abort still throws.
93
+ */
94
+ export async function probeHasAudio(source, options = {}) {
95
+ const probe = await probeOrNull(source, options);
96
+ return probe !== null && probe.audio !== null;
97
+ }
98
+ /**
99
+ * `source`'s primary video stream, or `null` when it has none or is not media
100
+ * Mediabunny reads. An abort still throws.
101
+ */
102
+ export async function probeVideoStream(source, options = {}) {
103
+ return (await probeOrNull(source, options))?.video ?? null;
104
+ }
105
+ /**
106
+ * Presentation timestamps of the primary video track's packets in seconds, in
107
+ * file (decode) order, as ffprobe's `packet=pts_time` lists them. `null` when
108
+ * there is no video track or the track has more than `maxPackets` packets.
109
+ */
110
+ export async function packetTimes(source, options = {}) {
111
+ const { signal } = options;
112
+ const maxPackets = options.maxPackets ?? Number.POSITIVE_INFINITY;
113
+ throwIfAborted(signal);
114
+ const input = openInput(source);
115
+ try {
116
+ const track = await input.getPrimaryVideoTrack();
117
+ if (!track)
118
+ return null;
119
+ const times = [];
120
+ const sink = new EncodedPacketSink(track);
121
+ for await (const packet of sink.packets(undefined, undefined, { metadataOnly: true })) {
122
+ throwIfAborted(signal);
123
+ if (times.length >= maxPackets)
124
+ return null;
125
+ times.push(packet.timestamp);
126
+ }
127
+ return times;
128
+ }
129
+ finally {
130
+ input.dispose();
131
+ }
132
+ }
133
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/media/probe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Raw RGBA frames in and out of libav with node-av: a bounded decode reader
3
+ * for the compositor's inputs and an encoder for its output.
4
+ */
5
+ /** One decoded frame as packed RGBA8. */
6
+ export interface RgbaFrame {
7
+ rgba: Uint8Array;
8
+ width: number;
9
+ height: number;
10
+ /** Presentation time after the seek, in seconds. */
11
+ ptsSec: number;
12
+ }
13
+ /** Frames read ahead of the consumer, so decode overlaps compositing. */
14
+ export interface RgbaFrameReader {
15
+ /** The next frame, or `null` once the source is exhausted. */
16
+ next(): Promise<RgbaFrame | null>;
17
+ /** Stop decoding and free the decoder. Waits for a decode in progress. */
18
+ close(): Promise<void>;
19
+ }
20
+ /**
21
+ * Decode `filePath`'s first video stream through `filter` and hand out the
22
+ * frames as RGBA. `filter` is a `-vf` chain that ends in `format=rgba`.
23
+ *
24
+ * `seekSec` reproduces the CLI's `-ss` before `-i`: seek to the keyframe at or
25
+ * before it, drop every frame before it, and count time from it. At most
26
+ * `readAhead` frames wait decoded; at 1080p each is 8 MB.
27
+ */
28
+ export declare function openRgbaFrameReader(opts: {
29
+ filePath: string;
30
+ filter: string;
31
+ seekSec?: number;
32
+ readAhead?: number;
33
+ signal?: AbortSignal;
34
+ }): Promise<RgbaFrameReader | null>;
35
+ /** Codec settings for {@link openVideoFrameEncoder}, the CLI's output arguments as data. */
36
+ export interface VideoEncoderSpec {
37
+ /** Encoder name, e.g. `libx264`, `prores_ks`, `libvpx-vp9`, `ffv1`. */
38
+ codec: string;
39
+ /** Pixel format the encoder receives, e.g. `yuv420p`. */
40
+ pixelFormat: string;
41
+ /** A `-vf` chain run before the pixel format conversion. */
42
+ filter?: string;
43
+ /**
44
+ * Encoder AVOptions: private ones (`preset`, `profile`) and generic ones
45
+ * (`b`, `level`, `colorspace`, `color_primaries`, `color_trc`).
46
+ */
47
+ options?: Record<string, string>;
48
+ }
49
+ /** A running encode that turns written frames into a finished file. */
50
+ export interface VideoFrameEncoder {
51
+ /** One frame: packed RGBA8 at the encoder's size, or one PNG, JPEG or WebP file. */
52
+ write(frame: Uint8Array): Promise<void>;
53
+ /** Flush the encoder and finalize the file. */
54
+ finish(): Promise<void>;
55
+ /** Stop and free everything. The file is left incomplete. */
56
+ abort(): Promise<void>;
57
+ }
58
+ /**
59
+ * Encode frames written at `fps` into `outPath`, the in-process form of
60
+ * `ffmpeg -f rawvideo -pix_fmt rgba -s WxH -r FPS -i pipe:0 <args> out` (and of
61
+ * `-f image2pipe` for `input: "png"`, which also takes JPEG and WebP files).
62
+ * Frames are converted by the
63
+ * spec's filter and then to its pixel format, as the CLI's `-vf` and
64
+ * `-pix_fmt` did.
65
+ */
66
+ export declare function openVideoFrameEncoder(opts: {
67
+ outPath: string;
68
+ fps: number;
69
+ input: "rgba" | "png";
70
+ /** Frame size. Required for RGBA input; a PNG carries its own. */
71
+ width?: number;
72
+ height?: number;
73
+ spec: VideoEncoderSpec;
74
+ /** Muxer AVOptions, e.g. `{ movflags: "+faststart" }`. */
75
+ muxerOptions?: Record<string, string>;
76
+ signal?: AbortSignal;
77
+ }): Promise<VideoFrameEncoder>;
78
+ //# sourceMappingURL=rgba.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rgba.d.ts","sourceRoot":"","sources":["../../src/media/rgba.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4BH,yCAAyC;AACzC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAClC,0EAA0E;IAC1E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAmFlC;AAED,4FAA4F;AAC5F,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAChC,oFAAoF;IACpF,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,+CAA+C;IAC/C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,6DAA6D;IAC7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA4DD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;IACtB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,gBAAgB,CAAC;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAmH7B"}
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Raw RGBA frames in and out of libav with node-av: a bounded decode reader
3
+ * for the compositor's inputs and an encoder for its output.
4
+ */
5
+ import { AV_CODEC_ID_MJPEG, AV_CODEC_ID_PNG, AV_CODEC_ID_WEBP, AV_PIX_FMT_RGBA, Codec, CodecContext, Encoder, FilterAPI, Frame, Packet, Rational } from "node-av";
6
+ import { abortError, findEncoder, openFileMuxer, quietLibav, rationalFor, throwIfAborted } from "./av.js";
7
+ import { openFilterGraph } from "./filter-graph.js";
8
+ /**
9
+ * Decode `filePath`'s first video stream through `filter` and hand out the
10
+ * frames as RGBA. `filter` is a `-vf` chain that ends in `format=rgba`.
11
+ *
12
+ * `seekSec` reproduces the CLI's `-ss` before `-i`: seek to the keyframe at or
13
+ * before it, drop every frame before it, and count time from it. At most
14
+ * `readAhead` frames wait decoded; at 1080p each is 8 MB.
15
+ */
16
+ export async function openRgbaFrameReader(opts) {
17
+ const seekSec = opts.seekSec && opts.seekSec > 0 ? opts.seekSec : 0;
18
+ const run = await openFilterGraph({
19
+ filePath: opts.filePath,
20
+ kind: "video",
21
+ // The decoder has already moved the seek point to zero, so `trim` drops
22
+ // the frames decoded on the way from the keyframe to it.
23
+ filter: seekSec > 0 ? `trim=start=0,${opts.filter}` : opts.filter,
24
+ seekSec,
25
+ signal: opts.signal
26
+ });
27
+ if (!run)
28
+ return null;
29
+ const readAhead = Math.max(1, opts.readAhead ?? 4);
30
+ const queue = [];
31
+ let failure = null;
32
+ let ended = false;
33
+ let closed = false;
34
+ let wakeConsumer = null;
35
+ let wakeProducer = null;
36
+ const wake = () => {
37
+ wakeConsumer?.();
38
+ wakeConsumer = null;
39
+ };
40
+ const pump = (async () => {
41
+ try {
42
+ for await (const frame of run.frames()) {
43
+ try {
44
+ const { timeBase } = frame;
45
+ queue.push({
46
+ rgba: new Uint8Array(frame.toBuffer()),
47
+ width: frame.width,
48
+ height: frame.height,
49
+ ptsSec: timeBase.den > 0 ? (Number(frame.pts) * timeBase.num) / timeBase.den : 0
50
+ });
51
+ }
52
+ finally {
53
+ frame.free();
54
+ }
55
+ wake();
56
+ while (queue.length >= readAhead && !closed) {
57
+ await new Promise((resolve) => {
58
+ wakeProducer = resolve;
59
+ });
60
+ }
61
+ if (closed)
62
+ break;
63
+ }
64
+ }
65
+ catch (error) {
66
+ failure = error;
67
+ }
68
+ finally {
69
+ ended = true;
70
+ wake();
71
+ }
72
+ })();
73
+ return {
74
+ async next() {
75
+ for (;;) {
76
+ if (closed)
77
+ return null;
78
+ const frame = queue.shift();
79
+ if (frame) {
80
+ wakeProducer?.();
81
+ wakeProducer = null;
82
+ return frame;
83
+ }
84
+ if (failure)
85
+ throw failure;
86
+ if (ended)
87
+ return null;
88
+ await new Promise((resolve) => {
89
+ wakeConsumer = resolve;
90
+ });
91
+ }
92
+ },
93
+ async close() {
94
+ if (closed)
95
+ return;
96
+ closed = true;
97
+ queue.length = 0;
98
+ wakeProducer?.();
99
+ wakeProducer = null;
100
+ wake();
101
+ await pump;
102
+ await run.close();
103
+ }
104
+ };
105
+ }
106
+ /** The decoder for an image file's bytes: PNG, JPEG or WebP. */
107
+ function imageCodecId(bytes) {
108
+ if (bytes[0] === 0xff && bytes[1] === 0xd8)
109
+ return AV_CODEC_ID_MJPEG;
110
+ if (Buffer.from(bytes.subarray(0, 4)).toString("latin1") === "RIFF" &&
111
+ Buffer.from(bytes.subarray(8, 12)).toString("latin1") === "WEBP") {
112
+ return AV_CODEC_ID_WEBP;
113
+ }
114
+ return AV_CODEC_ID_PNG;
115
+ }
116
+ /**
117
+ * Decodes standalone image files, one packet per file, as the image2 demuxer
118
+ * read them. Each file gets a fresh decoder: libav's PNG decoder keeps the
119
+ * last row of the previous image and applies it to the first row of the next
120
+ * when the size is unchanged, so one context adds each frame onto the last.
121
+ */
122
+ class ImageDecoder {
123
+ packet = new Packet();
124
+ allocated = false;
125
+ async decode(bytes) {
126
+ const id = imageCodecId(bytes);
127
+ const codec = Codec.findDecoder(id);
128
+ if (!codec)
129
+ throw new Error(`This libav build has no ${id} image decoder`);
130
+ const context = new CodecContext();
131
+ context.allocContext3(codec);
132
+ try {
133
+ const opened = await context.open2(codec, null);
134
+ if (opened < 0)
135
+ throw new Error(`Could not open the ${codec.name} decoder (${opened})`);
136
+ if (!this.allocated) {
137
+ this.packet.alloc();
138
+ this.allocated = true;
139
+ }
140
+ this.packet.data = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
141
+ const sent = await context.sendPacket(this.packet);
142
+ this.packet.unref();
143
+ if (sent < 0)
144
+ throw new Error(`The image decoder refused a frame (${sent})`);
145
+ const frame = new Frame();
146
+ frame.alloc();
147
+ const received = await context.receiveFrame(frame);
148
+ if (received < 0) {
149
+ frame.free();
150
+ throw new Error(`The image decoder returned no picture (${received})`);
151
+ }
152
+ return frame;
153
+ }
154
+ finally {
155
+ context.freeContext();
156
+ }
157
+ }
158
+ free() {
159
+ if (this.allocated)
160
+ this.packet.free();
161
+ this.allocated = false;
162
+ }
163
+ }
164
+ /**
165
+ * Encode frames written at `fps` into `outPath`, the in-process form of
166
+ * `ffmpeg -f rawvideo -pix_fmt rgba -s WxH -r FPS -i pipe:0 <args> out` (and of
167
+ * `-f image2pipe` for `input: "png"`, which also takes JPEG and WebP files).
168
+ * Frames are converted by the
169
+ * spec's filter and then to its pixel format, as the CLI's `-vf` and
170
+ * `-pix_fmt` did.
171
+ */
172
+ export async function openVideoFrameEncoder(opts) {
173
+ quietLibav();
174
+ throwIfAborted(opts.signal);
175
+ const { spec, signal } = opts;
176
+ if (opts.input === "rgba" && !(opts.width && opts.height)) {
177
+ throw new Error("RGBA input needs the frame size");
178
+ }
179
+ const codec = findEncoder(spec.codec);
180
+ if (!codec)
181
+ throw new Error(`This libav build has no ${spec.codec} encoder`);
182
+ const rate = rationalFor(opts.fps);
183
+ const timeBase = new Rational(rate.den, rate.num);
184
+ const filter = FilterAPI.create([spec.filter, `format=${spec.pixelFormat}`].filter(Boolean).join(","), { signal });
185
+ let encoder = null;
186
+ let output = null;
187
+ let streamIndex = -1;
188
+ const png = opts.input === "png" ? new ImageDecoder() : null;
189
+ let index = 0;
190
+ let failure = null;
191
+ let done = false;
192
+ const release = async () => {
193
+ if (done)
194
+ return;
195
+ done = true;
196
+ png?.free();
197
+ filter.close();
198
+ encoder?.close();
199
+ await output?.close();
200
+ };
201
+ const writePackets = async (frame) => {
202
+ if (!encoder || !output)
203
+ return;
204
+ for (const packet of await encoder.encodeAll(frame)) {
205
+ await output.muxer.writePacket(packet, streamIndex);
206
+ packet.free();
207
+ }
208
+ };
209
+ const encodeFiltered = async (frame) => {
210
+ for (const filtered of await filter.processAll(frame)) {
211
+ try {
212
+ if (!encoder) {
213
+ encoder = await Encoder.create(codec, {
214
+ options: spec.options,
215
+ context: { framerate: new Rational(rate.num, rate.den) },
216
+ signal
217
+ });
218
+ output = await openFileMuxer(opts.outPath, { muxerOptions: opts.muxerOptions, signal });
219
+ streamIndex = output.muxer.addStream(encoder);
220
+ }
221
+ await writePackets(filtered);
222
+ }
223
+ finally {
224
+ filtered.free();
225
+ }
226
+ }
227
+ };
228
+ const guard = async (work) => {
229
+ if (failure)
230
+ throw failure;
231
+ if (done)
232
+ throw new Error("The encoder is closed");
233
+ if (signal?.aborted) {
234
+ failure = abortError(signal);
235
+ await release();
236
+ throw failure;
237
+ }
238
+ try {
239
+ await work();
240
+ }
241
+ catch (error) {
242
+ failure = error;
243
+ await release();
244
+ throw error;
245
+ }
246
+ };
247
+ return {
248
+ write(bytes) {
249
+ return guard(async () => {
250
+ let frame;
251
+ if (png) {
252
+ frame = await png.decode(bytes);
253
+ }
254
+ else {
255
+ const width = opts.width ?? 0;
256
+ const height = opts.height ?? 0;
257
+ if (bytes.byteLength !== width * height * 4) {
258
+ throw new Error(`Expected a ${width}x${height} RGBA frame (${width * height * 4} bytes), got ${bytes.byteLength}`);
259
+ }
260
+ frame = Frame.fromVideoBuffer(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength), { width, height, format: AV_PIX_FMT_RGBA, timeBase });
261
+ }
262
+ frame.pts = BigInt(index);
263
+ frame.timeBase = timeBase;
264
+ index += 1;
265
+ try {
266
+ await encodeFiltered(frame);
267
+ }
268
+ finally {
269
+ frame.free();
270
+ }
271
+ });
272
+ },
273
+ finish() {
274
+ return guard(async () => {
275
+ await encodeFiltered(null);
276
+ await writePackets(null);
277
+ if (!output)
278
+ throw new Error("No frames were written");
279
+ await release();
280
+ });
281
+ },
282
+ abort: release
283
+ };
284
+ }
285
+ //# sourceMappingURL=rgba.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rgba.js","sourceRoot":"","sources":["../../src/media/rgba.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Test media without the ffmpeg CLI: generate inputs from libavfilter
3
+ * sources (`testsrc2`, `color`, `sine`, `anullsrc`) or from RGBA frames, and
4
+ * measure outputs (duration, size, frame times, pixels, PCM loudness).
5
+ *
6
+ * These run on the same node-av build the server uses, so a test that passes
7
+ * here needs no `ffmpeg` on PATH.
8
+ */
9
+ import { type PcmAudio } from "./audio.js";
10
+ import { type MediaProbe } from "./probe.js";
11
+ import { type VideoEncoderSpec } from "./rgba.js";
12
+ /** One generated stream: a lavfi source graph and how to encode it. */
13
+ export interface GeneratedStream {
14
+ /**
15
+ * A lavfi source with its own length, e.g.
16
+ * `testsrc2=size=64x48:rate=24:duration=2` or `sine=frequency=440:duration=2`.
17
+ */
18
+ source: string;
19
+ /** Encoder name. Defaults to `libx264` for video and `aac` for audio. */
20
+ codec?: string;
21
+ /** Video pixel format. Defaults to `yuv420p`. */
22
+ pixelFormat?: string;
23
+ /** Filters between the source and the encoder. */
24
+ filter?: string;
25
+ /** Encoder AVOptions, e.g. `{ g: "12", bf: "2", colorspace: "bt709" }`. */
26
+ options?: Record<string, string>;
27
+ }
28
+ /**
29
+ * Write a file holding the given streams, as
30
+ * `ffmpeg -f lavfi -i <video> -f lavfi -i <audio> <encoder args> out` did.
31
+ */
32
+ export declare function generateMedia(opts: {
33
+ outPath: string;
34
+ video?: GeneratedStream;
35
+ audio?: GeneratedStream;
36
+ /** Muxer AVOptions, e.g. `{ movflags: "+faststart" }`. */
37
+ muxerOptions?: Record<string, string>;
38
+ }): Promise<void>;
39
+ /**
40
+ * Encode frames produced by `frameAt` (packed RGBA8) at `fps`. Defaults to
41
+ * lossless FFV1 so a test reads back exactly what it wrote.
42
+ */
43
+ export declare function writeRgbaVideo(opts: {
44
+ outPath: string;
45
+ width: number;
46
+ height: number;
47
+ fps: number;
48
+ frames: number;
49
+ frameAt: (index: number) => Uint8Array;
50
+ spec?: VideoEncoderSpec;
51
+ }): Promise<void>;
52
+ /** A solid RGBA frame. */
53
+ export declare function solidRgba(width: number, height: number, [r, g, b, a]: readonly [number, number, number, number]): Uint8Array;
54
+ /** Write 16-bit PCM WAV bytes for interleaved float `samples`. */
55
+ export declare function encodeWav(samples: Float32Array, sampleRate: number, channels: number): Uint8Array;
56
+ /** Interleaved samples of a sine, or silence at `amplitude: 0`. */
57
+ export declare function sineSamples(opts: {
58
+ sampleRate: number;
59
+ channels: number;
60
+ durationSec: number;
61
+ frequency: number;
62
+ amplitude?: number;
63
+ }): Float32Array;
64
+ /** What a test asks of an output file. */
65
+ export interface MediaReport extends MediaProbe {
66
+ /** Video packet times in seconds, sorted. Empty without video. */
67
+ frameTimes: number[];
68
+ }
69
+ /** Probe `filePath` and list its video frame times. */
70
+ export declare function inspectMedia(filePath: string): Promise<MediaReport>;
71
+ /**
72
+ * Every video packet's presentation time (seconds, rounded to the
73
+ * microsecond as ffprobe prints `pts_time`) and keyframe flag, in
74
+ * presentation order.
75
+ */
76
+ export declare function videoPacketInfo(filePath: string): Promise<Array<{
77
+ pts: number;
78
+ key: boolean;
79
+ }>>;
80
+ /** Every video frame of `filePath` as RGBA at `width`x`height`, in order. */
81
+ export declare function readRgbaFrames(filePath: string, size: {
82
+ width: number;
83
+ height: number;
84
+ }): Promise<Uint8Array[]>;
85
+ /** Mean of each RGBA channel over a frame. */
86
+ export declare function meanRgba(rgba: Uint8Array): [number, number, number, number];
87
+ /** `filePath`'s audio as interleaved float PCM. */
88
+ export declare function readPcm(filePath: string, options: {
89
+ sampleRate: number;
90
+ channels?: number;
91
+ }): Promise<PcmAudio>;
92
+ /** RMS of `audio` between two times, over every channel. */
93
+ export declare function rmsBetween(audio: PcmAudio, startSec: number, endSec: number): number;
94
+ /** What ffprobe's `-show_streams` printed for a video stream, by its names. */
95
+ export interface VideoStreamFormat {
96
+ /** `h264`, `vp9`, `prores`. */
97
+ codec: string;
98
+ /** `yuv420p`, `yuva420p`, `yuv422p10le`. */
99
+ pixelFormat: string;
100
+ colorSpace: string;
101
+ colorPrimaries: string;
102
+ colorTransfer: string;
103
+ colorRange: string;
104
+ /** Stream tags, e.g. Matroska's `alpha_mode`. */
105
+ tags: Record<string, string>;
106
+ }
107
+ /** The first video stream's codec, pixel format and tags, or null without one. */
108
+ export declare function videoStreamFormat(filePath: string): Promise<VideoStreamFormat | null>;
109
+ /**
110
+ * Every video frame of `filePath` as RGBA at `width`x`height`, decoded by the
111
+ * named libav decoder rather than the default one. A test of VP9 alpha needs
112
+ * `libvpx-vp9`: libav's native vp9 decoder drops the alpha plane WebM keeps
113
+ * in BlockAdditions, as `ffmpeg -c:v libvpx-vp9 -i` was needed to read it.
114
+ */
115
+ export declare function readRgbaFramesWith(filePath: string, decoderName: string, size: {
116
+ width: number;
117
+ height: number;
118
+ }): Promise<Uint8Array[]>;
119
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/media/testing.ts"],"names":[],"mappings":"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"}