'use strict'; const fs = require('node:fs'); const path = require('node:path'); const { chromium } = require('playwright'); async function main() { const outputPath = process.argv[2]; if (!outputPath) throw new Error('usage: node generate_mediarecorder_fixture.js OUTPUT.json'); const browser = await chromium.launch({ headless: true, args: ['--autoplay-policy=no-user-gesture-required'], }); try { const page = await browser.newPage(); const fixture = await page.evaluate(async () => { const mimeType = 'audio/webm;codecs=opus'; if (!MediaRecorder.isTypeSupported(mimeType)) { throw new Error(`${mimeType} is not supported by this Chromium build`); } const context = new AudioContext({sampleRate: 48000}); const destination = context.createMediaStreamDestination(); const oscillator = context.createOscillator(); const gain = context.createGain(); oscillator.frequency.value = 440; gain.gain.value = 0; oscillator.connect(gain).connect(destination); oscillator.start(); const chunks = []; const recorder = new MediaRecorder(destination.stream, {mimeType}); recorder.ondataavailable = event => { if (event.data && event.data.size) chunks.push(event.data); }; const stopped = new Promise(resolve => { recorder.onstop = resolve; }); const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)); recorder.start(250); await delay(1400); gain.gain.setValueAtTime(0.35, context.currentTime); await delay(900); gain.gain.setValueAtTime(0, context.currentTime); await delay(500); recorder.stop(); await stopped; oscillator.stop(); await context.close(); const encoded = []; for (const chunk of chunks) { const bytes = new Uint8Array(await chunk.arrayBuffer()); let binary = ''; for (let index = 0; index < bytes.length; index += 0x8000) { binary += String.fromCharCode(...bytes.subarray(index, index + 0x8000)); } encoded.push(btoa(binary)); } return { mime_type: recorder.mimeType, timeslice_ms: 250, pre_speech_chunk_count: 5, chunks_base64: encoded, }; }); const chunks = fixture.chunks_base64.map(value => Buffer.from(value, 'base64')); const payloadPath = outputPath.replace(/\.json$/i, '.webm'); fixture.chunk_sizes = chunks.map(chunk => chunk.length); fixture.payload_file = path.basename(payloadPath); delete fixture.chunks_base64; fixture.browser = await browser.version(); fixture.generator = 'testing/probes/generate_mediarecorder_fixture.js'; fs.writeFileSync(payloadPath, Buffer.concat(chunks)); fs.writeFileSync(outputPath, `${JSON.stringify(fixture, null, 2)}\n`); } finally { await browser.close(); } } main().catch(error => { console.error(error.stack || error); process.exitCode = 1; });