1test("getConstantAspectScaleFilter picks one square-pixel ratio for the ladder", () => {
2 const filter = getConstantAspectScaleFilter({ width: 1920, height: 817 }, 600);
3 assert.equal(filter, "scale=1410:600");
4});
5
6test("getAv1VideoArgs uses constant-aspect scaling for downscaled renditions", () => {
7 const args = getAv1VideoArgs(
8 { id: "al", preset: 2, mbitMax: 1.25, crf: 40, maxHeight: 600 },
9 { width: 1920, height: 817 },
10 ["-i", "/tmp/input.mp4"],
11 new Path("/tmp/out"),
12 );
13
14 assert.equal(args[args.indexOf("-vf") + 1], "scale=1410:600");
15 assert.equal(args[args.indexOf("-y")], "-y");
16 assert.equal(args.at(-1), "/tmp/out/tmp.av1.mp4");
17});
18
19test("getAv1VideoArgs also snaps top-quality renditions onto the same exact ratio", () => {
20 const args = getAv1VideoArgs(
21 { id: "au", preset: 1, mbitMax: 30, crf: 28, depth: 10 },
22 { width: 1920, height: 817 },
23 ["-i", "/tmp/input.mp4"],
24 new Path("/tmp/out"),
25 );
26
27 assert.equal(args[args.indexOf("-vf") + 1], "scale=1880:800");
28});
29
30test("getMpegDashArgs keeps all video streams in one adaptation set", () => {
31 const args = getMpegDashArgs(
32 [
33 new Path("/tmp/av1-1.mp4"),
34 new Path("/tmp/av1-2.mp4"),
35 new Path("/tmp/av1-3.mp4"),
36 ],
37 [
38 new Path("/tmp/audio-1.webm"),
39 new Path("/tmp/audio-2.webm"),
40 ],
41 new Path("/tmp/dash"),
42 );
43
44 const adaptationSets = args[args.indexOf("-adaptation_sets") + 1];
45 assert.equal(adaptationSets, "id=0,streams=0,1,2 id=1,streams=3,4");
46});
47
48test("all AV1 presets share one exact square-pixel ratio across many source sizes", () => {
49 const sources = [
50 { width: 3840, height: 2160 },
51 { width: 1920, height: 1080 },
52 { width: 1920, height: 817 },
53 { width: 1440, height: 1080 },
54 { width: 1280, height: 720 },
55 { width: 1280, height: 534 },
56 { width: 1080, height: 1920 },
57 { width: 1080, height: 1350 },
58 { width: 1024, height: 768 },
59 { width: 853, height: 480 },
60 { width: 800, height: 800 },
61 { width: 721, height: 405 },
62 { width: 640, height: 360 },
63 { width: 426, height: 240 },
64 ];
65
66 for (const source of sources) {
67 let expectedRatio: number | null = null;
68 for (const preset of videoFormats) {
69 const args = getAv1VideoArgs(
70 preset,
71 source,
72 ["-i", "/tmp/input.mp4"],
73 new Path("/tmp/out"),
74 );
75 const vfIndex = args.indexOf("-vf");
76 const filter = vfIndex === -1 ? null : args[vfIndex + 1];
77 const dims = filter == null
78 ? source
79 : parseScaleFilter(assertString(filter));
80
81 assert.ok(dims.width > 0);
82 assert.ok(dims.height > 0);
83 assert.equal(dims.width % 2, 0);
84 assert.equal(dims.height % 2, 0);
85 assert.ok(dims.width <= source.width);
86 assert.ok(dims.height <= source.height);
87 if (preset.maxHeight != null) {
88 assert.ok(
89 dims.height <= preset.maxHeight,
90 `${source.width}x${source.height} exceeded maxHeight ${preset.maxHeight} with ${dims.width}x${dims.height}`,
91 );
92 }
93
94 const ratio = dims.width / dims.height;
95 if (expectedRatio == null) {
96 expectedRatio = ratio;
97 } else {
98 assert.equal(
99 ratio,
100 expectedRatio,
101 `${source.width}x${source.height} produced mismatched ladder ratio ${dims.width}x${dims.height}`,
102 );
103 }
104 }
105 }
106});
107
108test("canonical ratio sweep matches the best feasible exact ratio", () => {
109 for (let width = 320; width <= 4096; width += 113) {
110 for (let height = 240; height <= 3072; height += 97) {
111 const source = { width, height };
112 const actualDimsByPreset: Array<{ width: number; height: number }> = [];
113 let referenceDims: { width: number; height: number } | null = null;
114
115 for (const preset of videoFormats) {
116 const args = getAv1VideoArgs(
117 preset,
118 source,
119 ["-i", "/tmp/input.mp4"],
120 new Path("/tmp/out"),
121 );
122 const vfIndex = args.indexOf("-vf");
123 const filter = vfIndex === -1 ? null : args[vfIndex + 1];
124 const dims = filter == null
125 ? source
126 : parseScaleFilter(assertString(filter));
127
128 actualDimsByPreset.push(dims);
129
130 if (referenceDims == null) {
131 referenceDims = dims;
132 } else {
133 assert.equal(
134 dims.width * referenceDims.height,
135 referenceDims.width * dims.height,
136 `${width}x${height} produced inconsistent exact ratios within the ladder`,
137 );
138 }
139 }
140
141 const actualRatio = reduceRatio(referenceDims!);
142 const actualScore = getCandidateScore(source, actualRatio);
143 assert.ok(actualScore, `${width}x${height} produced an infeasible ratio`);
144 const feasibleCandidates = getFeasibleCandidates(source);
145 const candidatesWithinThreshold = feasibleCandidates.filter((candidate) =>
146 candidate.error <= EXACT_ERROR_THRESHOLD
147 );
148
149 if (candidatesWithinThreshold.length > 0) {
150 assert.ok(
151 actualScore.error <= EXACT_ERROR_THRESHOLD,
152 `${width}x${height} should have found a ratio within the allowed drift threshold`,
153 );
154 } else {
155 const bestFallbackError = Math.min(
156 ...feasibleCandidates.map((candidate) => candidate.error),
157 );
158 assert.equal(
159 actualScore.error,
160 bestFallbackError,
161 `${width}x${height} did not pick a minimum-drift fallback ratio`,
162 );
163 }
164
165 for (let i = 0; i < videoFormats.length; i++) {
166 const expectedDims = getExpectedDimensionsForPreset(
167 source,
168 actualRatio,
169 videoFormats[i].maxHeight,
170 );
171 assert.deepEqual(
172 actualDimsByPreset[i],
173 expectedDims,
174 `${width}x${height} produced unexpected dimensions for preset ${videoFormats[i].id}`,
175 );
176 }
177 }
178 }
179});
180
181test("dense sweep never produces mismatched ladder aspect ratios", () => {
182 for (let width = 2; width <= 2048; width++) {
183 for (let height = 2; height <= 2048; height++) {
184 const source = { width, height };
185 let referenceDims: { width: number; height: number } | null = null;
186
187 for (const preset of videoFormats) {
188 const args = getAv1VideoArgs(
189 preset,
190 source,
191 ["-i", "/tmp/input.mp4"],
192 new Path("/tmp/out"),
193 );
194 const vfIndex = args.indexOf("-vf");
195 const filter = vfIndex === -1 ? null : args[vfIndex + 1];
196 const dims = filter == null
197 ? source
198 : parseScaleFilter(assertString(filter));
199
200 assert.ok(dims.width >= 2, `${width}x${height} produced width < 2`);
201 assert.ok(dims.height >= 2, `${width}x${height} produced height < 2`);
202 if (filter != null) {
203 assert.equal(dims.width % 2, 0, `${width}x${height} produced odd scaled width`);
204 assert.equal(dims.height % 2, 0, `${width}x${height} produced odd scaled height`);
205 }
206 assert.ok(dims.width <= source.width, `${width}x${height} exceeded source width`);
207 assert.ok(dims.height <= source.height, `${width}x${height} exceeded source height`);
208 if (preset.maxHeight != null) {
209 assert.ok(
210 dims.height <= preset.maxHeight,
211 `${width}x${height} exceeded maxHeight ${preset.maxHeight} with ${dims.width}x${dims.height}`,
212 );
213 }
214
215 if (referenceDims == null) {
216 referenceDims = dims;
217 } else {
218 assert.equal(
219 dims.width * referenceDims.height,
220 referenceDims.width * dims.height,
221 `${width}x${height} produced mismatched exact ratios: ${referenceDims.width}x${referenceDims.height} vs ${dims.width}x${dims.height}`,
222 );
223 }
224 }
225 }
226 }
227});
228
229import { Path } from "#sitegen/path";
230import assert from "node:assert";
231import { test } from "vitest";
232import { getAv1VideoArgs, getConstantAspectScaleFilter, getMpegDashArgs, videoFormats } from "./transcode-rules.ts";
233
234function assertString(value: string | undefined | null): string {
235 assert.equal(typeof value, "string");
236 return value;
237}
238
239function parseScaleFilter(filter: string) {
240 const match = /^scale=(\d+):(\d+)$/.exec(filter);
241 assert.ok(match, `unexpected filter ${filter}`);
242 return {
243 width: Number(match[1]),
244 height: Number(match[2]),
245 };
246}
247
248function getExpectedDimensionsForPreset(
249 source: { width: number; height: number },
250 ratio: { width: number; height: number },
251 maxHeight?: number,
252) {
253 const heightCap = Math.min(maxHeight ?? source.height, source.height);
254 const maxScale = Math.floor(Math.min(
255 source.width / ratio.width,
256 heightCap / ratio.height,
257 ));
258 const scale = maxScale % 2 === 0 ? maxScale : maxScale - 1;
259 const dims = {
260 width: ratio.width * scale,
261 height: ratio.height * scale,
262 };
263
264 if (dims.width === source.width && dims.height === source.height) return source;
265 return dims;
266}
267
268const EXACT_ERROR_THRESHOLD = 0.0005;
269
270function getFeasibleCandidates(source: { width: number; height: number }) {
271 const caps = [
272 source.height,
273 ...videoFormats.flatMap((preset) => preset.maxHeight != null ? [Math.min(preset.maxHeight, source.height)] : []),
274 ];
275 const sourceRatio = source.width / source.height;
276 const maxDenominator = Math.max(1, Math.floor(Math.min(...caps) / 2));
277 const seen = new Set<string>();
278 const candidates: Array<{
279 width: number;
280 height: number;
281 error: number;
282 retainedArea: number;
283 }> = [];
284
285 for (let denominator = 1; denominator <= maxDenominator; denominator++) {
286 const numerator = Math.max(1, Math.round(sourceRatio * denominator));
287 const divisor = greatestCommonDivisor(numerator, denominator);
288 const candidate = {
289 width: numerator / divisor,
290 height: denominator / divisor,
291 };
292 const key = `${candidate.width}:${candidate.height}`;
293 if (seen.has(key)) continue;
294 seen.add(key);
295
296 const dims = videoFormats.map((preset) => getExpectedDimensionsForPreset(source, candidate, preset.maxHeight));
297 if (dims.some((value) => value.width <= 0 || value.height <= 0)) continue;
298
299 const retainedArea = dims
300 .map((value) => value.width * value.height)
301 .reduce((sum, value) => sum + value, 0);
302 const error = Math.abs((candidate.width / candidate.height) - sourceRatio)
303 / sourceRatio;
304 candidates.push({ ...candidate, error, retainedArea });
305 }
306
307 return candidates;
308}
309
310function greatestCommonDivisor(a: number, b: number): number {
311 let x = Math.abs(a);
312 let y = Math.abs(b);
313 while (y !== 0) {
314 [x, y] = [y, x % y];
315 }
316 return x || 1;
317}
318
319function reduceRatio(dimensions: { width: number; height: number }) {
320 const divisor = greatestCommonDivisor(dimensions.width, dimensions.height);
321 return {
322 width: dimensions.width / divisor,
323 height: dimensions.height / divisor,
324 };
325}
326
327function getCandidateScore(
328 source: { width: number; height: number },
329 ratio: { width: number; height: number },
330) {
331 const dims = videoFormats.map((preset) => getExpectedDimensionsForPreset(source, ratio, preset.maxHeight));
332 if (dims.some((value) => value.width <= 0 || value.height <= 0)) return null;
333 const sourceRatio = source.width / source.height;
334 return {
335 ...ratio,
336 error: Math.abs((ratio.width / ratio.height) - sourceRatio) / sourceRatio,
337 retainedArea: dims
338 .map((value) => value.width * value.height)
339 .reduce((sum, value) => sum + value, 0),
340 };
341}