gitframes 1.3.0 → 1.3.1
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/dist/audio/index.mjs +4 -4
- package/dist/{audio-C4kiPbrV.mjs → audio-DvL7gQQ8.mjs} +3 -3
- package/dist/{audio-C4kiPbrV.mjs.map → audio-DvL7gQQ8.mjs.map} +1 -1
- package/dist/{audio-CiXUMGyk.mjs → audio-sZdRh_qg.mjs} +3 -3
- package/dist/{audio-CiXUMGyk.mjs.map → audio-sZdRh_qg.mjs.map} +1 -1
- package/dist/{dist-C37594c6.mjs → dist-8vbhZYqX.mjs} +471 -152
- package/dist/dist-8vbhZYqX.mjs.map +1 -0
- package/dist/{dist-CMF9eNLB.mjs → dist-B0vCkDzJ.mjs} +37 -3
- package/dist/dist-B0vCkDzJ.mjs.map +1 -0
- package/dist/effects/index.mjs +2 -2
- package/dist/{effects-C9connag.mjs → effects-C3nV-k4T.mjs} +4 -4
- package/dist/{effects-C9connag.mjs.map → effects-C3nV-k4T.mjs.map} +1 -1
- package/dist/fonts/index.mjs +1 -1
- package/dist/{fonts-Dn18r_ST.mjs → fonts-dn-pl4Pe.mjs} +3 -3
- package/dist/{fonts-Dn18r_ST.mjs.map → fonts-dn-pl4Pe.mjs.map} +1 -1
- package/dist/{index-YZsTjqut.d.mts → index-D-O2kwLx.d.mts} +3 -3
- package/dist/{index-YZsTjqut.d.mts.map → index-D-O2kwLx.d.mts.map} +1 -1
- package/dist/index.d.mts +67 -49
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +96 -52
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.mjs +2 -2
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +3 -3
- package/dist/{renderer-DgY7XBYE.mjs → renderer-CMDIVxhv.mjs} +315 -12
- package/dist/renderer-CMDIVxhv.mjs.map +1 -0
- package/dist/{renderers-CLJQRPVU-CMZnHNK0.mjs → renderers-CLJQRPVU-DuQ7-PaC.mjs} +3 -3
- package/dist/{renderers-CLJQRPVU-CMZnHNK0.mjs.map → renderers-CLJQRPVU-DuQ7-PaC.mjs.map} +1 -1
- package/dist/signals/index.mjs +5 -5
- package/dist/{signals-BB_kdndF.mjs → signals-CLaCq092.mjs} +2 -2
- package/dist/{signals-BB_kdndF.mjs.map → signals-CLaCq092.mjs.map} +1 -1
- package/package.json +52 -52
- package/dist/dist-C37594c6.mjs.map +0 -1
- package/dist/dist-CMF9eNLB.mjs.map +0 -1
- package/dist/renderer-DgY7XBYE.mjs.map +0 -1
package/dist/react/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as drawCompositionTree, o as useCompositionState, t as CanvasComposition } from "../dist-
|
|
2
|
-
import {
|
|
1
|
+
import { i as drawCompositionTree, o as useCompositionState, t as CanvasComposition } from "../dist-B0vCkDzJ.mjs";
|
|
2
|
+
import { at as useRenderContext, g as RenderProvider } from "../dist-8vbhZYqX.mjs";
|
|
3
3
|
|
|
4
4
|
export { CanvasComposition, CanvasComposition as GitframesPlayer, RenderProvider, drawCompositionTree, useCompositionState, useRenderContext };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { FrameGridOptions, FrameGridRenderable, FrameSamplePoint, HeadlessMediaRenderer, HeadlessWebGPURenderer, computeGridLayout, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resolveSamplePoints };
|
|
1
|
+
import { _ as resolveSamplePoints, a as QaSegment, c as VideoQaStats, d as renderSemaphore, f as FrameGridOptions, g as renderFrameGrid, h as computeGridLayout, i as QaIssue, l as formatQaReport, m as FrameSamplePoint, n as HeadlessMediaRenderer, o as VideoQaOptions, p as FrameGridRenderable, r as HeadlessWebGPURenderer, s as VideoQaReport, t as AudioQaStats, u as initHeadlessWebGPU } from "../index-D-O2kwLx.mjs";
|
|
2
|
+
export { AudioQaStats, FrameGridOptions, FrameGridRenderable, FrameSamplePoint, HeadlessMediaRenderer, HeadlessWebGPURenderer, QaIssue, QaSegment, VideoQaOptions, VideoQaReport, VideoQaStats, computeGridLayout, formatQaReport, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resolveSamplePoints };
|
package/dist/renderer/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../chunk-GMkBE123.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as HeadlessWebGPURenderer, i as HeadlessMediaRenderer, n as renderFrameGrid, o as
|
|
2
|
+
import { W as initHeadlessWebGPU } from "../dist-8vbhZYqX.mjs";
|
|
3
|
+
import { a as HeadlessWebGPURenderer, i as HeadlessMediaRenderer, n as renderFrameGrid, o as formatQaReport, r as resolveSamplePoints, s as renderSemaphore, t as computeGridLayout } from "../renderer-CMDIVxhv.mjs";
|
|
4
4
|
import "../dist-cjs-Bsz2eGzl.mjs";
|
|
5
5
|
|
|
6
|
-
export { HeadlessMediaRenderer, HeadlessWebGPURenderer, computeGridLayout, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resolveSamplePoints };
|
|
6
|
+
export { HeadlessMediaRenderer, HeadlessWebGPURenderer, computeGridLayout, formatQaReport, initHeadlessWebGPU, renderFrameGrid, renderSemaphore, resolveSamplePoints };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, o as __toESM, t as __commonJSMin } from "./chunk-GMkBE123.mjs";
|
|
2
|
-
import { C as preloadFont, I as registerWebGPURenderer, P as audioRegistry, Sn as
|
|
3
|
-
import { H as
|
|
2
|
+
import { C as preloadFont, I as registerWebGPURenderer, P as audioRegistry, Sn as takeRenderDiagnostics, a as mixAudioTracks, i as drawCompositionTree, n as compositionStateStore, on as getMediaType, wn as updateClockSignals } from "./dist-B0vCkDzJ.mjs";
|
|
3
|
+
import { B as ensureDevice, G as inputStore, H as getHeadlessFontPath, O as clearAllVideoCache, S as SlugFontCache, T as WebGPUAudioProcessor, U as getRenderer2D, W as initHeadlessWebGPU, X as mediaDecoderCache, Y as lutStore, it as textureCache, m as NodeSurfaceProvider, nt as shaderStore, ot as GetFontAssetUrl, st as R2_CUSTOM_DOMAIN } from "./dist-8vbhZYqX.mjs";
|
|
4
4
|
import { t as require_dist_cjs$20 } from "./dist-cjs-CslF8Dju.mjs";
|
|
5
5
|
import { D as require_dist_cjs$21, O as dist_es_exports$1, _ as require_dist_cjs$33, a as require_dist_cjs$26, b as init_protocols, c as require_dist_cjs$37, d as require_dist_cjs$32, f as require_dist_cjs$23, g as require_dist_cjs$34, h as require_dist_cjs$35, i as require_dist_cjs$28, k as init_dist_es$1, l as require_dist_cjs$27, m as require_dist_cjs$22, n as require_dist_cjs$31, o as require_dist_cjs$29, p as require_dist_cjs$24, r as require_dist_cjs$30, s as require_dist_cjs$25, t as require_dist_cjs$38, u as require_dist_cjs$36, v as dist_es_exports, x as protocols_exports, y as init_dist_es } from "./dist-cjs-Bsz2eGzl.mjs";
|
|
6
6
|
import { F as require_dist_cjs$42, I as require_dist_cjs$44, P as require_dist_cjs$39, R as require_dist_cjs$43, T as schema_exports, t as require_dist_cjs$41, v as init_tslib_es6, w as init_schema, y as tslib_es6_exports, z as require_dist_cjs$40 } from "./dist-cjs-DssYqayv.mjs";
|
|
@@ -21747,7 +21747,7 @@ S3StorageService = __decorate([
|
|
|
21747
21747
|
], S3StorageService);
|
|
21748
21748
|
|
|
21749
21749
|
//#endregion
|
|
21750
|
-
//#region ../renderer/dist/bootstrap-mediabunny-
|
|
21750
|
+
//#region ../renderer/dist/bootstrap-mediabunny-CMP9YRF6.mjs
|
|
21751
21751
|
/**
|
|
21752
21752
|
* Recursively scans and preloads only the required font assets inside a VirtualMediaData tree.
|
|
21753
21753
|
* This ensures typography renders correctly on Node.js without pre-loading or memory bloating from large media assets.
|
|
@@ -21919,7 +21919,7 @@ var DmaStagingRing = class {
|
|
|
21919
21919
|
* bypassing redundant intermediate JS allocations when row pitches align,
|
|
21920
21920
|
* and immediately unmaps the buffer.
|
|
21921
21921
|
*/
|
|
21922
|
-
async readFrameToVideoFrame(frameIndex, timestampUs, colorSpace) {
|
|
21922
|
+
async readFrameToVideoFrame(frameIndex, timestampUs, colorSpace, inspect) {
|
|
21923
21923
|
this.checkDestroyed();
|
|
21924
21924
|
const slot = this.slots[frameIndex % this.capacity];
|
|
21925
21925
|
if (!slot) throw new Error(`Invalid slot index for frame ${frameIndex}`);
|
|
@@ -21937,6 +21937,7 @@ var DmaStagingRing = class {
|
|
|
21937
21937
|
pixelData.set(sourceView.subarray(srcOffset, srcOffset + this.unpaddedBytesPerRow), dstOffset);
|
|
21938
21938
|
}
|
|
21939
21939
|
}
|
|
21940
|
+
inspect?.(pixelData);
|
|
21940
21941
|
const vf = new VideoFrame(pixelData, {
|
|
21941
21942
|
format: "RGBA",
|
|
21942
21943
|
codedWidth: this.width,
|
|
@@ -22180,7 +22181,290 @@ const bootstrapMediabunny = () => {
|
|
|
22180
22181
|
};
|
|
22181
22182
|
|
|
22182
22183
|
//#endregion
|
|
22183
|
-
//#region ../renderer/dist/headless-webgpu-renderer-
|
|
22184
|
+
//#region ../renderer/dist/headless-webgpu-renderer-C7euEl4e.mjs
|
|
22185
|
+
const DEFAULTS = {
|
|
22186
|
+
blackPixelThreshold: .1,
|
|
22187
|
+
blackFrameRatio: .98,
|
|
22188
|
+
minBlackSec: .5,
|
|
22189
|
+
minFrozenSec: 2,
|
|
22190
|
+
silenceDbfs: -60,
|
|
22191
|
+
minSilenceSec: 2,
|
|
22192
|
+
lufsTolerance: 1
|
|
22193
|
+
};
|
|
22194
|
+
const SAMPLE_STEP = 4;
|
|
22195
|
+
const FROZEN_DIFF = .25;
|
|
22196
|
+
/** Measures frames handed to it in order, as RGBA bytes. */
|
|
22197
|
+
var FrameInspector = class {
|
|
22198
|
+
blackLuma;
|
|
22199
|
+
blackRatio;
|
|
22200
|
+
black = [];
|
|
22201
|
+
sameAsPrevious = [];
|
|
22202
|
+
lumaSum = 0;
|
|
22203
|
+
previous = null;
|
|
22204
|
+
constructor(options = {}) {
|
|
22205
|
+
this.blackLuma = (options.blackPixelThreshold ?? DEFAULTS.blackPixelThreshold) * 255;
|
|
22206
|
+
this.blackRatio = options.blackFrameRatio ?? DEFAULTS.blackFrameRatio;
|
|
22207
|
+
}
|
|
22208
|
+
get frameCount() {
|
|
22209
|
+
return this.black.length;
|
|
22210
|
+
}
|
|
22211
|
+
inspect(rgba, width, height) {
|
|
22212
|
+
const cols = Math.ceil(width / SAMPLE_STEP);
|
|
22213
|
+
const rows = Math.ceil(height / SAMPLE_STEP);
|
|
22214
|
+
const luma = new Uint8Array(cols * rows);
|
|
22215
|
+
let blackCount = 0;
|
|
22216
|
+
let sum = 0;
|
|
22217
|
+
let k$2 = 0;
|
|
22218
|
+
for (let y$1 = 0; y$1 < height; y$1 += SAMPLE_STEP) {
|
|
22219
|
+
let p$1 = y$1 * width * 4;
|
|
22220
|
+
for (let x$2 = 0; x$2 < width; x$2 += SAMPLE_STEP, p$1 += SAMPLE_STEP * 4) {
|
|
22221
|
+
const l$1 = .2126 * rgba[p$1] + .7152 * rgba[p$1 + 1] + .0722 * rgba[p$1 + 2];
|
|
22222
|
+
luma[k$2++] = l$1;
|
|
22223
|
+
sum += l$1;
|
|
22224
|
+
if (l$1 <= this.blackLuma) blackCount++;
|
|
22225
|
+
}
|
|
22226
|
+
}
|
|
22227
|
+
const n$2 = luma.length;
|
|
22228
|
+
this.lumaSum += sum / n$2 / 255;
|
|
22229
|
+
this.black.push(blackCount / n$2 >= this.blackRatio);
|
|
22230
|
+
let same = false;
|
|
22231
|
+
if (this.previous && this.previous.length === n$2) {
|
|
22232
|
+
let diff = 0;
|
|
22233
|
+
for (let i$1 = 0; i$1 < n$2; i$1++) diff += Math.abs(luma[i$1] - this.previous[i$1]);
|
|
22234
|
+
same = diff / n$2 < FROZEN_DIFF;
|
|
22235
|
+
}
|
|
22236
|
+
this.sameAsPrevious.push(same);
|
|
22237
|
+
this.previous = luma;
|
|
22238
|
+
}
|
|
22239
|
+
stats(expectedFrames, encodedFrames, fps, width, height, options = {}) {
|
|
22240
|
+
const minBlack = Math.max(1, Math.round((options.minBlackSec ?? DEFAULTS.minBlackSec) * fps));
|
|
22241
|
+
const minFrozen = Math.max(2, Math.round((options.minFrozenSec ?? DEFAULTS.minFrozenSec) * fps));
|
|
22242
|
+
const blackSegments = runs(this.black, minBlack).map(([a$2, b$1]) => ({
|
|
22243
|
+
startSec: a$2 / fps,
|
|
22244
|
+
endSec: b$1 / fps
|
|
22245
|
+
}));
|
|
22246
|
+
const frozenSegments = runs(this.sameAsPrevious, minFrozen - 1).map(([a$2, b$1]) => ({
|
|
22247
|
+
startSec: (a$2 - 1) / fps,
|
|
22248
|
+
endSec: b$1 / fps
|
|
22249
|
+
}));
|
|
22250
|
+
return {
|
|
22251
|
+
expectedFrames,
|
|
22252
|
+
encodedFrames,
|
|
22253
|
+
fps,
|
|
22254
|
+
width,
|
|
22255
|
+
height,
|
|
22256
|
+
meanLuma: this.frameCount > 0 ? this.lumaSum / this.frameCount : 0,
|
|
22257
|
+
blackSegments,
|
|
22258
|
+
frozenSegments
|
|
22259
|
+
};
|
|
22260
|
+
}
|
|
22261
|
+
};
|
|
22262
|
+
/** [start, end) index ranges where flags hold for at least minLength in a row. */
|
|
22263
|
+
function runs(flags, minLength) {
|
|
22264
|
+
const out = [];
|
|
22265
|
+
let start = -1;
|
|
22266
|
+
for (let i$1 = 0; i$1 <= flags.length; i$1++) if (i$1 < flags.length && flags[i$1]) {
|
|
22267
|
+
if (start < 0) start = i$1;
|
|
22268
|
+
} else if (start >= 0) {
|
|
22269
|
+
if (i$1 - start >= minLength) out.push([start, i$1]);
|
|
22270
|
+
start = -1;
|
|
22271
|
+
}
|
|
22272
|
+
return out;
|
|
22273
|
+
}
|
|
22274
|
+
const toDb = (power) => power > 0 ? 10 * Math.log10(power) : -Infinity;
|
|
22275
|
+
/** Measures a mixed audio track (one Float32Array per channel). */
|
|
22276
|
+
function analyzeAudio(channels, sampleRate, options = {}) {
|
|
22277
|
+
const length = channels[0]?.length ?? 0;
|
|
22278
|
+
let peak = 0;
|
|
22279
|
+
let sumSquares = 0;
|
|
22280
|
+
let clipped = 0;
|
|
22281
|
+
for (const ch of channels) for (let i$1 = 0; i$1 < length; i$1++) {
|
|
22282
|
+
const v$1 = Math.abs(ch[i$1]);
|
|
22283
|
+
if (v$1 > peak) peak = v$1;
|
|
22284
|
+
if (v$1 >= .9999) clipped++;
|
|
22285
|
+
sumSquares += v$1 * v$1;
|
|
22286
|
+
}
|
|
22287
|
+
const total = length * Math.max(1, channels.length);
|
|
22288
|
+
const silenceDbfs = options.silenceDbfs ?? DEFAULTS.silenceDbfs;
|
|
22289
|
+
const window = Math.max(1, Math.round(sampleRate * .1));
|
|
22290
|
+
const silent = [];
|
|
22291
|
+
for (let start = 0; start < length; start += window) {
|
|
22292
|
+
const end = Math.min(length, start + window);
|
|
22293
|
+
let s = 0;
|
|
22294
|
+
for (const ch of channels) for (let i$1 = start; i$1 < end; i$1++) s += ch[i$1] * ch[i$1];
|
|
22295
|
+
silent.push(toDb(s / ((end - start) * channels.length)) < silenceDbfs);
|
|
22296
|
+
}
|
|
22297
|
+
const silentSegments = runs(silent, Math.max(1, Math.round((options.minSilenceSec ?? DEFAULTS.minSilenceSec) / .1))).map(([a$2, b$1]) => ({
|
|
22298
|
+
startSec: a$2 * window / sampleRate,
|
|
22299
|
+
endSec: Math.min(length, b$1 * window) / sampleRate
|
|
22300
|
+
}));
|
|
22301
|
+
const peakDbfs = 20 * Math.log10(Math.max(peak, 1e-12));
|
|
22302
|
+
return {
|
|
22303
|
+
sampleRate,
|
|
22304
|
+
channels: channels.length,
|
|
22305
|
+
durationSec: length / sampleRate,
|
|
22306
|
+
peakDbfs,
|
|
22307
|
+
rmsDbfs: toDb(sumSquares / Math.max(1, total)),
|
|
22308
|
+
integratedLufs: peakDbfs < silenceDbfs ? null : integratedLoudness(channels, sampleRate),
|
|
22309
|
+
clippedSamples: clipped,
|
|
22310
|
+
silentSegments
|
|
22311
|
+
};
|
|
22312
|
+
}
|
|
22313
|
+
/** BS.1770 K-weighting stage 1: +4 dB high shelf around 1.5 kHz (the head). */
|
|
22314
|
+
function highShelf(sampleRate) {
|
|
22315
|
+
const A$2 = 10 ** (4 / 40);
|
|
22316
|
+
const w0 = 2 * Math.PI * 1500 / sampleRate;
|
|
22317
|
+
const cos = Math.cos(w0);
|
|
22318
|
+
const alpha = Math.sin(w0) / (2 / Math.SQRT2);
|
|
22319
|
+
const sq = 2 * Math.sqrt(A$2) * alpha;
|
|
22320
|
+
const a0 = A$2 + 1 - (A$2 - 1) * cos + sq;
|
|
22321
|
+
return {
|
|
22322
|
+
b0: A$2 * (A$2 + 1 + (A$2 - 1) * cos + sq) / a0,
|
|
22323
|
+
b1: -2 * A$2 * (A$2 - 1 + (A$2 + 1) * cos) / a0,
|
|
22324
|
+
b2: A$2 * (A$2 + 1 + (A$2 - 1) * cos - sq) / a0,
|
|
22325
|
+
a1: 2 * (A$2 - 1 - (A$2 + 1) * cos) / a0,
|
|
22326
|
+
a2: (A$2 + 1 - (A$2 - 1) * cos - sq) / a0
|
|
22327
|
+
};
|
|
22328
|
+
}
|
|
22329
|
+
/** BS.1770 K-weighting stage 2: 38 Hz high pass, Q 0.5 (the RLB curve). */
|
|
22330
|
+
function highPass(sampleRate) {
|
|
22331
|
+
const w0 = 2 * Math.PI * 38 / sampleRate;
|
|
22332
|
+
const cos = Math.cos(w0);
|
|
22333
|
+
const alpha = Math.sin(w0) / (2 * .5);
|
|
22334
|
+
const a0 = 1 + alpha;
|
|
22335
|
+
return {
|
|
22336
|
+
b0: (1 + cos) / 2 / a0,
|
|
22337
|
+
b1: -(1 + cos) / a0,
|
|
22338
|
+
b2: (1 + cos) / 2 / a0,
|
|
22339
|
+
a1: -2 * cos / a0,
|
|
22340
|
+
a2: (1 - alpha) / a0
|
|
22341
|
+
};
|
|
22342
|
+
}
|
|
22343
|
+
function filter(input, f$1) {
|
|
22344
|
+
const out = new Float32Array(input.length);
|
|
22345
|
+
let x1 = 0;
|
|
22346
|
+
let x2 = 0;
|
|
22347
|
+
let y1 = 0;
|
|
22348
|
+
let y2 = 0;
|
|
22349
|
+
for (let i$1 = 0; i$1 < input.length; i$1++) {
|
|
22350
|
+
const x$2 = input[i$1];
|
|
22351
|
+
const y$1 = f$1.b0 * x$2 + f$1.b1 * x1 + f$1.b2 * x2 - f$1.a1 * y1 - f$1.a2 * y2;
|
|
22352
|
+
out[i$1] = y$1;
|
|
22353
|
+
x2 = x1;
|
|
22354
|
+
x1 = x$2;
|
|
22355
|
+
y2 = y1;
|
|
22356
|
+
y1 = y$1;
|
|
22357
|
+
}
|
|
22358
|
+
return out;
|
|
22359
|
+
}
|
|
22360
|
+
/**
|
|
22361
|
+
* ITU-R BS.1770-4 integrated loudness (LUFS): K-weighted mean square in
|
|
22362
|
+
* 400 ms blocks with 75% overlap, gated at -70 LUFS and then at 10 LU below
|
|
22363
|
+
* the mean of what passed. Channels weigh 1 (stereo and mono layouts).
|
|
22364
|
+
*/
|
|
22365
|
+
function integratedLoudness(channels, sampleRate) {
|
|
22366
|
+
const shelf = highShelf(sampleRate);
|
|
22367
|
+
const hp = highPass(sampleRate);
|
|
22368
|
+
const weighted = channels.map((ch) => filter(filter(ch, shelf), hp));
|
|
22369
|
+
const length = weighted[0]?.length ?? 0;
|
|
22370
|
+
const block = Math.round(.4 * sampleRate);
|
|
22371
|
+
const hop = Math.round(.1 * sampleRate);
|
|
22372
|
+
if (length < block) return null;
|
|
22373
|
+
const blockPower = [];
|
|
22374
|
+
for (let start = 0; start + block <= length; start += hop) {
|
|
22375
|
+
let power = 0;
|
|
22376
|
+
for (const ch of weighted) {
|
|
22377
|
+
let s = 0;
|
|
22378
|
+
for (let i$1 = start; i$1 < start + block; i$1++) s += ch[i$1] * ch[i$1];
|
|
22379
|
+
power += s / block;
|
|
22380
|
+
}
|
|
22381
|
+
blockPower.push(power);
|
|
22382
|
+
}
|
|
22383
|
+
const lufs = (power) => -.691 + toDb(power);
|
|
22384
|
+
const mean = (xs) => xs.reduce((a$2, b$1) => a$2 + b$1, 0) / xs.length;
|
|
22385
|
+
const absolute = blockPower.filter((p$1) => lufs(p$1) > -70);
|
|
22386
|
+
if (absolute.length === 0) return null;
|
|
22387
|
+
const relativeGate = lufs(mean(absolute)) - 10;
|
|
22388
|
+
const gated = absolute.filter((p$1) => lufs(p$1) > relativeGate);
|
|
22389
|
+
return gated.length > 0 ? lufs(mean(gated)) : null;
|
|
22390
|
+
}
|
|
22391
|
+
/** Turn measurements and document diagnostics into a report with issues. */
|
|
22392
|
+
function buildQaReport(parts) {
|
|
22393
|
+
const { video, audio, options = {} } = parts;
|
|
22394
|
+
const issues = [];
|
|
22395
|
+
const at$1 = (s) => `${s.startSec.toFixed(2)}–${s.endSec.toFixed(2)} s`;
|
|
22396
|
+
if (video) {
|
|
22397
|
+
if (video.encodedFrames !== video.expectedFrames) issues.push({
|
|
22398
|
+
severity: "error",
|
|
22399
|
+
code: "frames_missing",
|
|
22400
|
+
message: `${video.encodedFrames} of ${video.expectedFrames} frames were encoded`
|
|
22401
|
+
});
|
|
22402
|
+
for (const s of video.blackSegments) issues.push({
|
|
22403
|
+
severity: "warning",
|
|
22404
|
+
code: "black_frames",
|
|
22405
|
+
message: `Black frames at ${at$1(s)}`,
|
|
22406
|
+
...s
|
|
22407
|
+
});
|
|
22408
|
+
for (const s of video.frozenSegments) issues.push({
|
|
22409
|
+
severity: "info",
|
|
22410
|
+
code: "frozen_video",
|
|
22411
|
+
message: `Picture does not change at ${at$1(s)}`,
|
|
22412
|
+
...s
|
|
22413
|
+
});
|
|
22414
|
+
}
|
|
22415
|
+
if (audio) if (audio.integratedLufs === null) issues.push({
|
|
22416
|
+
severity: "info",
|
|
22417
|
+
code: "audio_silent",
|
|
22418
|
+
message: "The audio track is silent"
|
|
22419
|
+
});
|
|
22420
|
+
else {
|
|
22421
|
+
if (audio.clippedSamples > 0) issues.push({
|
|
22422
|
+
severity: "warning",
|
|
22423
|
+
code: "audio_clipping",
|
|
22424
|
+
message: `${audio.clippedSamples} audio sample(s) at or beyond full scale (peak ${audio.peakDbfs.toFixed(1)} dBFS)`
|
|
22425
|
+
});
|
|
22426
|
+
for (const s of audio.silentSegments) issues.push({
|
|
22427
|
+
severity: "info",
|
|
22428
|
+
code: "audio_silence",
|
|
22429
|
+
message: `Silence at ${at$1(s)}`,
|
|
22430
|
+
...s
|
|
22431
|
+
});
|
|
22432
|
+
if (options.targetLufs !== void 0) {
|
|
22433
|
+
const off = audio.integratedLufs - options.targetLufs;
|
|
22434
|
+
const tolerance = options.lufsTolerance ?? DEFAULTS.lufsTolerance;
|
|
22435
|
+
if (Math.abs(off) > tolerance) issues.push({
|
|
22436
|
+
severity: "warning",
|
|
22437
|
+
code: "loudness_off_target",
|
|
22438
|
+
message: `Integrated loudness ${audio.integratedLufs.toFixed(1)} LUFS is ${Math.abs(off).toFixed(1)} LU ${off > 0 ? "above" : "below"} the ${options.targetLufs} LUFS target`
|
|
22439
|
+
});
|
|
22440
|
+
}
|
|
22441
|
+
}
|
|
22442
|
+
for (const d$1 of parts.diagnostics ?? []) issues.push({
|
|
22443
|
+
severity: d$1.severity,
|
|
22444
|
+
code: d$1.code,
|
|
22445
|
+
message: d$1.message,
|
|
22446
|
+
...d$1.layerId !== void 0 && { layerId: d$1.layerId }
|
|
22447
|
+
});
|
|
22448
|
+
return {
|
|
22449
|
+
passed: !issues.some((i$1) => i$1.severity !== "info"),
|
|
22450
|
+
...video && { video },
|
|
22451
|
+
...audio && { audio },
|
|
22452
|
+
issues
|
|
22453
|
+
};
|
|
22454
|
+
}
|
|
22455
|
+
/** A short human- and agent-readable summary of a report. */
|
|
22456
|
+
function formatQaReport(report) {
|
|
22457
|
+
const lines = [`QA ${report.passed ? "passed" : "FAILED"}`];
|
|
22458
|
+
const v$1 = report.video;
|
|
22459
|
+
if (v$1) lines.push(` video: ${v$1.encodedFrames}/${v$1.expectedFrames} frames, ${v$1.width}x${v$1.height} @ ${v$1.fps} fps, mean luma ${v$1.meanLuma.toFixed(2)}`);
|
|
22460
|
+
const a$2 = report.audio;
|
|
22461
|
+
if (a$2) {
|
|
22462
|
+
const lufs = a$2.integratedLufs === null ? "silent" : `${a$2.integratedLufs.toFixed(1)} LUFS`;
|
|
22463
|
+
lines.push(` audio: ${a$2.durationSec.toFixed(2)} s, ${lufs}, peak ${a$2.peakDbfs.toFixed(1)} dBFS, rms ${a$2.rmsDbfs.toFixed(1)} dBFS`);
|
|
22464
|
+
}
|
|
22465
|
+
for (const i$1 of report.issues) lines.push(` ${i$1.severity.padEnd(7)} ${i$1.code}: ${i$1.message}`);
|
|
22466
|
+
return lines.join("\n");
|
|
22467
|
+
}
|
|
22184
22468
|
/**
|
|
22185
22469
|
* High-performance hardware video encoding pipeline integrating WebGPU textures with WebCodecs.
|
|
22186
22470
|
*
|
|
@@ -22199,6 +22483,7 @@ var ZeroCopyWebCodecsPipeline = class {
|
|
|
22199
22483
|
stagingRing;
|
|
22200
22484
|
colorSpace;
|
|
22201
22485
|
directCanvasSource;
|
|
22486
|
+
onFrame;
|
|
22202
22487
|
inFlightFrames = [];
|
|
22203
22488
|
cycleStartTimes = /* @__PURE__ */ new Map();
|
|
22204
22489
|
totalFramesEnqueued = 0;
|
|
@@ -22218,6 +22503,7 @@ var ZeroCopyWebCodecsPipeline = class {
|
|
|
22218
22503
|
this.ringCapacity = Math.max(2, options.ringCapacity ?? 2);
|
|
22219
22504
|
this.colorSpace = options.colorSpace;
|
|
22220
22505
|
this.directCanvasSource = options.directCanvasSource;
|
|
22506
|
+
this.onFrame = options.onFrame;
|
|
22221
22507
|
this.stagingRing = new DmaStagingRing(device, {
|
|
22222
22508
|
width: this.width,
|
|
22223
22509
|
height: this.height,
|
|
@@ -22317,7 +22603,8 @@ var ZeroCopyWebCodecsPipeline = class {
|
|
|
22317
22603
|
sample.close();
|
|
22318
22604
|
}
|
|
22319
22605
|
} else {
|
|
22320
|
-
const
|
|
22606
|
+
const onFrame = this.onFrame;
|
|
22607
|
+
const vf = await this.stagingRing.readFrameToVideoFrame(frameIndex, timestampUs, this.colorSpace, onFrame && ((rgba) => onFrame(rgba, frameIndex)));
|
|
22321
22608
|
try {
|
|
22322
22609
|
const sample = new VideoSample(vf, {
|
|
22323
22610
|
duration: 1 / this.fps,
|
|
@@ -22600,7 +22887,7 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
22600
22887
|
renderer,
|
|
22601
22888
|
surface
|
|
22602
22889
|
};
|
|
22603
|
-
updateClockSignals(frame, fps, virtualMedia.metadata.durationMs);
|
|
22890
|
+
updateClockSignals(frame, fps, virtualMedia.metadata.durationMs ?? void 0);
|
|
22604
22891
|
if (frame === 0 || lutStore.hasPending(device) || SlugFontCache.hasPending()) {
|
|
22605
22892
|
const preEncoder = device.createCommandEncoder();
|
|
22606
22893
|
const dummyTex = renderer.getTemporaryTexture(width, height);
|
|
@@ -22838,6 +23125,10 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
22838
23125
|
}
|
|
22839
23126
|
const totalFrames = Math.max(1, Math.round(durationMs / 1e3 * fps));
|
|
22840
23127
|
const renderId = `vid-${randomUUID()}`;
|
|
23128
|
+
const qaOptions = options?.qa === true ? {} : options?.qa ? options.qa : void 0;
|
|
23129
|
+
const inspector = qaOptions ? new FrameInspector(qaOptions) : void 0;
|
|
23130
|
+
let videoQa;
|
|
23131
|
+
let audioQa;
|
|
22841
23132
|
const videoCodec = codecOption === "vp9" ? "vp9" : codecOption === "vp8" ? "vp8" : "avc";
|
|
22842
23133
|
const audioCodec = options?.audioCodec ?? (isMp3 ? "mp3" : isWebM ? "opus" : "aac");
|
|
22843
23134
|
const quality = resolveQuality(options?.quality);
|
|
@@ -22940,7 +23231,8 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
22940
23231
|
height,
|
|
22941
23232
|
fps,
|
|
22942
23233
|
videoSource,
|
|
22943
|
-
ringCapacity: 2
|
|
23234
|
+
ringCapacity: 2,
|
|
23235
|
+
onFrame: inspector ? (rgba) => inspector.inspect(rgba, width, height) : void 0
|
|
22944
23236
|
});
|
|
22945
23237
|
const ctx = surface && renderer ? {
|
|
22946
23238
|
device,
|
|
@@ -23004,6 +23296,7 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
23004
23296
|
if (pipeline) {
|
|
23005
23297
|
await pipeline.drain();
|
|
23006
23298
|
const pipelineStats = pipeline.getStats();
|
|
23299
|
+
if (inspector && qaOptions) videoQa = inspector.stats(totalFrames, pipelineStats.totalFramesEncoded, fps, width, height, qaOptions);
|
|
23007
23300
|
rendererLogger.debug({
|
|
23008
23301
|
renderId,
|
|
23009
23302
|
totalFrames,
|
|
@@ -23014,6 +23307,7 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
23014
23307
|
const { channels, sampleRate } = await mixAudioTracks(virtualMedia, fps, 48e3, device, renderId);
|
|
23015
23308
|
const numChannels = channels.length;
|
|
23016
23309
|
if (numChannels === 0) throw new Error("mixAudioTracks returned no channels");
|
|
23310
|
+
if (qaOptions) audioQa = analyzeAudio(channels, sampleRate, qaOptions);
|
|
23017
23311
|
const totalSamples = channels[0].length;
|
|
23018
23312
|
const chunkSize = Math.round(.1 * sampleRate);
|
|
23019
23313
|
for (let offset = 0; offset < totalSamples; offset += chunkSize) {
|
|
@@ -23035,6 +23329,12 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
23035
23329
|
}
|
|
23036
23330
|
await output.finalize();
|
|
23037
23331
|
outputFinalized = true;
|
|
23332
|
+
const qa = qaOptions ? buildQaReport({
|
|
23333
|
+
video: videoQa,
|
|
23334
|
+
audio: audioQa,
|
|
23335
|
+
diagnostics: takeRenderDiagnostics(renderId),
|
|
23336
|
+
options: qaOptions
|
|
23337
|
+
}) : void 0;
|
|
23038
23338
|
const cleanup = async () => {
|
|
23039
23339
|
try {
|
|
23040
23340
|
await fs$1.unlink(tempFilePath).catch(() => {});
|
|
@@ -23046,12 +23346,14 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
23046
23346
|
await convertMp4ToGif(tempFilePath, tempGifPath);
|
|
23047
23347
|
return {
|
|
23048
23348
|
filePath: tempGifPath,
|
|
23049
|
-
cleanup
|
|
23349
|
+
cleanup,
|
|
23350
|
+
...qa && { qa }
|
|
23050
23351
|
};
|
|
23051
23352
|
}
|
|
23052
23353
|
return {
|
|
23053
23354
|
filePath: tempFilePath,
|
|
23054
|
-
cleanup
|
|
23355
|
+
cleanup,
|
|
23356
|
+
...qa && { qa }
|
|
23055
23357
|
};
|
|
23056
23358
|
} catch (error) {
|
|
23057
23359
|
if (outputStarted && !outputFinalized) try {
|
|
@@ -23088,6 +23390,7 @@ var HeadlessWebGPURenderer = class HeadlessWebGPURenderer$1 {
|
|
|
23088
23390
|
try {
|
|
23089
23391
|
shaderStore.clear(renderId);
|
|
23090
23392
|
} catch {}
|
|
23393
|
+
takeRenderDiagnostics(renderId);
|
|
23091
23394
|
renderSemaphore.release();
|
|
23092
23395
|
rendererLogger.debug({
|
|
23093
23396
|
renderId,
|
|
@@ -23270,5 +23573,5 @@ async function renderFrameGrid(target, options = {}) {
|
|
|
23270
23573
|
}
|
|
23271
23574
|
|
|
23272
23575
|
//#endregion
|
|
23273
|
-
export { HeadlessWebGPURenderer as a, HeadlessMediaRenderer as i, renderFrameGrid as n,
|
|
23274
|
-
//# sourceMappingURL=renderer-
|
|
23576
|
+
export { HeadlessWebGPURenderer as a, HeadlessMediaRenderer as i, renderFrameGrid as n, formatQaReport as o, resolveSamplePoints as r, renderSemaphore as s, computeGridLayout as t };
|
|
23577
|
+
//# sourceMappingURL=renderer-CMDIVxhv.mjs.map
|