import { spawn } from 'node:child_process'; import { createHash } from 'node:crypto'; import { readFile, stat } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const SCRIPT_PATH = fileURLToPath(import.meta.url); const REPOSITORY_ROOT = path.resolve(path.dirname(SCRIPT_PATH), '..'); const FIXTURE_RELATIVE_PATH = 'tests/fixtures/generated/tone.wav'; const FIXTURE_PATH = path.join(REPOSITORY_ROOT, FIXTURE_RELATIVE_PATH); const EXPECTED_FIXTURE_BYTES = 32_102; const EXPECTED_FIXTURE_SHA256 = '0fd1f54d6be2f1cc7fd778775dd29cf184770d3716cf3394bf03ed54ac264c3c'; const DEFAULT_MODE_TIMEOUT_MILLISECONDS = 60_000; const RESULT_PREFIX = 'OPUS_RESULT '; const EVENT_PREFIX = 'OPUS_EVENT '; const MODES = Object.freeze(['single-thread', 'multithread']); const argumentsSet = new Set(process.argv.slice(2)); const option = (name) => process.argv .slice(2) .find((argument) => argument.startsWith(`--${name}=`)) ?.slice(name.length + 3); function boundedInteger(value, fallback, minimum, maximum) { const parsed = Number(value); return Number.isSafeInteger(parsed) && parsed >= minimum && parsed <= maximum ? parsed : fallback; } function serializeError(error) { if (error instanceof Error) { return { name: error.name, message: error.message, ...(error.stack ? { stack: error.stack.split('\n').slice(0, 8) } : {}), }; } return { name: 'Error', message: String(error) }; } function emit(prefix, value) { process.stdout.write(`${prefix}${JSON.stringify(value)}\n`); } function parsePrefixedJsonLines(text, prefix) { return text .split(/\r?\n/u) .filter((line) => line.startsWith(prefix)) .flatMap((line) => { try { return [JSON.parse(line.slice(prefix.length))]; } catch { return []; } }); } function appendBounded(current, chunk, maximumLength = 32_768) { return `${current}${chunk}`.slice(-maximumLength); } function terminateProcessGroup(child) { if (!child.pid) return; try { if (process.platform === 'win32') { child.kill('SIGKILL'); } else { process.kill(-child.pid, 'SIGKILL'); } } catch { child.kill('SIGKILL'); } } async function runIsolatedMode(mode, origin, timeoutMilliseconds) { const startedAt = performance.now(); const child = spawn( process.execPath, [SCRIPT_PATH, '--child', `--mode=${mode}`, `--origin=${origin}`], { cwd: REPOSITORY_ROOT, detached: process.platform !== 'win32', stdio: ['ignore', 'pipe', 'pipe'], } ); let stdout = ''; let stderr = ''; let timedOut = false; child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8'); child.stdout.on('data', (chunk) => { stdout = appendBounded(stdout, chunk); for (const event of parsePrefixedJsonLines(chunk, EVENT_PREFIX)) { if (event.stage && event.stage !== 'core-log') { process.stdout.write(`[opus:${mode}] ${event.stage}\n`); } } }); child.stderr.on('data', (chunk) => { stderr = appendBounded(stderr, chunk); }); const timeout = setTimeout(() => { timedOut = true; terminateProcessGroup(child); }, timeoutMilliseconds); const { code, signal } = await new Promise((resolve) => { child.once('close', (exitCode, exitSignal) => { resolve({ code: exitCode, signal: exitSignal }); }); }); clearTimeout(timeout); const elapsedMilliseconds = Math.round(performance.now() - startedAt); const events = parsePrefixedJsonLines(stdout, EVENT_PREFIX); const reported = parsePrefixedJsonLines(stdout, RESULT_PREFIX).at(-1); if (reported) { return { ...reported, elapsedMilliseconds, process: { exitCode: code, signal }, }; } const coreReady = events.find((event) => event.stage === 'core-ready'); const lastStage = [...events] .reverse() .find((event) => event.stage && event.stage !== 'core-log'); const relevantLogs = events .filter((event) => event.stage === 'core-log') .slice(-16) .map((event) => event.message); return { requestedMode: mode, status: 'fail', failure: timedOut ? 'timeout' : 'child-exited-without-result', message: timedOut ? `The isolated browser did not finish within ${String(timeoutMilliseconds)} ms.` : 'The isolated browser exited without a structured result.', ...(coreReady?.actualMode ? { actualMode: coreReady.actualMode } : {}), ...(coreReady?.ffmpegVersion ? { ffmpegVersion: coreReady.ffmpegVersion } : {}), ...(coreReady?.capabilities ? { capabilities: coreReady.capabilities } : {}), ...(lastStage?.stage ? { lastStage: lastStage.stage } : {}), ...(relevantLogs.length > 0 ? { relevantLogs } : {}), ...(stderr.trim() ? { stderr: stderr.trim().split('\n').slice(-12) } : {}), elapsedMilliseconds, process: { exitCode: code, signal }, }; } async function fixtureIdentity() { const [bytes, fixtureStat] = await Promise.all([ readFile(FIXTURE_PATH), stat(FIXTURE_PATH), ]); const sha256 = createHash('sha256').update(bytes).digest('hex'); if ( fixtureStat.size !== EXPECTED_FIXTURE_BYTES || sha256 !== EXPECTED_FIXTURE_SHA256 ) { throw new Error( `Opus fixture identity mismatch: expected ${String(EXPECTED_FIXTURE_BYTES)} bytes/${EXPECTED_FIXTURE_SHA256}, received ${String(fixtureStat.size)} bytes/${sha256}.` ); } return { path: FIXTURE_RELATIVE_PATH, bytes: fixtureStat.size, sha256, }; } async function runMain() { const timeoutMilliseconds = boundedInteger( option('timeout-ms') ?? process.env.AV_OPUS_REGRESSION_TIMEOUT_MS, DEFAULT_MODE_TIMEOUT_MILLISECONDS, 10_000, 10 * 60_000 ); const [fixture, packageText, coreLockText] = await Promise.all([ fixtureIdentity(), readFile(path.join(REPOSITORY_ROOT, 'package.json'), 'utf8'), readFile( path.join(REPOSITORY_ROOT, 'scripts', 'reviewed-ffmpeg-core-lock.json'), 'utf8' ), ]); const packageManifest = JSON.parse(packageText); const coreLock = JSON.parse(coreLockText); const { createServer } = await import('vite'); const server = await createServer({ configFile: path.join(REPOSITORY_ROOT, 'vite.config.ts'), logLevel: 'warn', server: { host: '127.0.0.1', port: 0, strictPort: false, }, }); try { await server.listen(); const address = server.httpServer?.address(); if (!address || typeof address === 'string') { throw new Error('Vite did not expose a local TCP address.'); } const origin = `http://127.0.0.1:${String(address.port)}`; const modes = []; for (const mode of MODES) { modes.push(await runIsolatedMode(mode, origin, timeoutMilliseconds)); } const report = { schemaVersion: 2, recordedAt: new Date().toISOString(), browser: 'chromium', wrapperVersion: packageManifest.dependencies['@ffmpeg/ffmpeg'], coreVersion: coreLock.coreVersion, coreBuildId: coreLock.buildId, coreProfile: coreLock.profile, fixture, perModeTimeoutMilliseconds: timeoutMilliseconds, modes, safe: modes.every((result) => result.status === 'pass'), }; process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); if (argumentsSet.has('--require-pass') && !report.safe) { process.exitCode = 1; } } finally { await server.close(); } } async function runBrowserMode(mode, origin) { const { chromium } = await import('@playwright/test'); const browser = await chromium.launch({ headless: true }); try { const page = await browser.newPage(); await page.exposeFunction('__reportOpusEvent', (event) => { emit(EVENT_PREFIX, event); }); await page.goto(origin, { waitUntil: 'domcontentloaded', timeout: 30_000, }); return await page.evaluate( async ({ fixtureUrl, requestedMode }) => { const report = async (event) => { await globalThis.__reportOpusEvent(event); }; const replacePaths = (plan, context) => { const replacements = new Map(); plan.inputs.forEach((input, index) => { const actual = context.inputPaths[index]; if (actual) replacements.set(input.path, actual); }); plan.outputs.forEach((output, index) => { const actual = context.outputPaths[index]; if (actual) replacements.set(output.path, actual); }); plan.temporaryFiles.forEach((temporary, index) => { const actual = context.temporaryPaths[index]; if (actual) replacements.set(temporary.path, actual); }); return plan.args.map((argument) => [...replacements].reduce( (resolved, [virtualPath, actualPath]) => resolved.replaceAll(virtualPath, actualPath), argument ) ); }; const errorShape = (error) => error instanceof Error ? { name: error.name, message: error.message, ...(error.stack ? { stack: error.stack.split('\n').slice(0, 8) } : {}), } : { name: 'Error', message: String(error) }; let manager; const relevantLogs = []; try { await report({ stage: 'module-import' }); const [{ EngineManager }, { buildConvertPlan }, { AUDIO_PRESETS }] = await Promise.all([ import('/src/ffmpeg/EngineManager.ts'), import('/src/commands/convert.ts'), import('/src/presets/audio-presets.ts'), ]); manager = new EngineManager(); manager.setPreference( requestedMode === 'single-thread' ? 'force-single-thread' : 'prefer-multithread' ); manager.onLog((event) => { for (const line of event.message.split(/\r?\n/u)) { if ( relevantLogs.length < 32 && /libopus|Output #|Stream mapping|Aborted\(\)|(?:^|\s)(?:size|progress)=|error|failed/iu.test( line ) ) { const message = line.slice(0, 600); relevantLogs.push(message); void report({ stage: 'core-log', message }); } } }); await report({ stage: 'fixture-fetch' }); const fixtureResponse = await fetch(fixtureUrl); if (!fixtureResponse.ok) { throw new Error( `Fixture request failed with HTTP ${String(fixtureResponse.status)}.` ); } const fixtureBytes = new Uint8Array( await fixtureResponse.arrayBuffer() ); const sourceFile = new File([fixtureBytes], 'tone.wav', { type: 'audio/wav', }); await report({ stage: 'core-initializing' }); await manager.initialize(); const ready = manager.state; if (ready.status !== 'ready') { throw new Error('The FFmpeg engine did not reach ready state.'); } const capabilities = { opusMuxer: ready.capabilities.muxers.has('opus'), libopusEncoder: ready.capabilities.encoders.has('libopus'), }; await report({ stage: 'core-ready', requestedMode, actualMode: ready.mode, ffmpegVersion: ready.capabilities.versionText, capabilities, }); if (ready.mode !== requestedMode) { throw new Error( `Requested ${requestedMode}, but the engine selected ${ready.mode}.` ); } if (!capabilities.opusMuxer || !capabilities.libopusEncoder) { throw new Error( 'The pinned core does not advertise the Opus muxer and libopus encoder.' ); } const preset = AUDIO_PRESETS.find( (candidate) => candidate.id === 'audio-opus' ); if (!preset) { throw new Error('The built-in audio-opus preset is missing.'); } const plan = buildConvertPlan({ jobId: `opus-regression-${requestedMode}`, source: { id: 'tone', sourceIndex: 0, fileName: 'tone.wav', extension: 'wav', }, preset, streamSelection: { audio: [0] }, expectedDurationSeconds: 1, timeoutMs: 30_000, }); await report({ stage: 'encode-started', presetId: preset.id, requirements: plan.requiredCapabilities, }); let commandArguments = []; const completed = await manager.runJob({ id: `opus-regression-${requestedMode}`, operation: 'Opus regression', coreWorkload: plan.operation, inputs: [sourceFile], outputs: plan.outputs.map((output) => ({ name: output.fileName, mimeType: output.mediaType, })), expectedDurationSeconds: plan.expectedDurationSeconds, timeoutMilliseconds: plan.timeoutMs, buildArguments: (context) => { commandArguments = replacePaths(plan, context); void report({ stage: 'encode-command', arguments: commandArguments, }); return commandArguments; }, }); const output = completed.outputs[0]; if (!output || output.bytes.byteLength === 0) { throw new Error( 'The Opus encode did not produce non-empty output.' ); } await report({ stage: 'encode-finished', outputBytes: output.bytes.byteLength, }); const outputFile = new File([output.bytes], output.name, { type: output.mimeType, }); await report({ stage: 'reprobe-started' }); const probe = await manager.probe(outputFile, 15_000); const probeJson = probe.json; const streams = Array.isArray(probeJson?.streams) ? probeJson.streams : []; const opusStream = streams.find( (stream) => stream?.codec_type === 'audio' && stream?.codec_name === 'opus' ); const durationSeconds = Number( probeJson?.format?.duration ?? opusStream?.duration ); if (!opusStream || !(durationSeconds > 0)) { throw new Error( 'The generated file did not re-probe as positive-duration Opus audio.' ); } const digest = new Uint8Array( await crypto.subtle.digest('SHA-256', output.bytes) ); const outputSha256 = [...digest] .map((value) => value.toString(16).padStart(2, '0')) .join(''); await report({ stage: 'reprobe-finished', durationSeconds }); return { requestedMode, actualMode: ready.mode, status: 'pass', ffmpegVersion: ready.capabilities.versionText, capabilities, presetId: preset.id, commandArguments, output: { name: output.name, mimeType: output.mimeType, bytes: output.bytes.byteLength, sha256: outputSha256, codecName: opusStream.codec_name, codecType: opusStream.codec_type, durationSeconds, }, relevantLogs, }; } catch (error) { const state = manager?.state; return { requestedMode, ...(state?.mode ? { actualMode: state.mode } : {}), status: 'fail', failure: 'browser-operation-failed', error: errorShape(error), relevantLogs, }; } finally { manager?.dispose(); } }, { fixtureUrl: `${origin}/${FIXTURE_RELATIVE_PATH}`, requestedMode: mode, } ); } finally { await browser.close(); } } async function runChild() { const mode = option('mode'); const origin = option('origin'); if (!MODES.includes(mode) || !origin) { throw new Error('Child mode requires valid --mode and --origin options.'); } emit(EVENT_PREFIX, { stage: 'browser-launching', requestedMode: mode }); try { const result = await runBrowserMode(mode, origin); emit(RESULT_PREFIX, result); } catch (error) { emit(RESULT_PREFIX, { requestedMode: mode, status: 'fail', failure: 'browser-process-failed', error: serializeError(error), }); } } try { if (argumentsSet.has('--child')) { await runChild(); } else { await runMain(); } } catch (error) { process.stderr.write( `Opus regression harness failed: ${JSON.stringify(serializeError(error))}\n` ); process.exitCode = 2; }