### Install TurboRes via npm Source: https://github.com/vanilagy/turbores/blob/main/README.md Install the TurboRes library using npm for use in your project. ```bash npm install turbores ``` -------------------------------- ### Benchmark Native FFmpeg Script Source: https://github.com/vanilagy/turbores/blob/main/benchmark/README.md This shell script benchmarks native FFmpeg decoding speed. It accepts thread count and an optional flag for hardware acceleration on macOS. Ensure you have FFmpeg installed and VideoToolbox available for hardware acceleration testing. ```bash ./benchmark-ffmpeg-native.sh -t [-a] ``` -------------------------------- ### Access Decoded Frame Properties Source: https://github.com/vanilagy/turbores/blob/main/README.md Inspect the properties of a successfully decoded frame to get information about its dimensions, pixel format, and color space. ```typescript result.frameData; // => Uint8Array result.pixelFormat; // => 'I422P10' result.scanType; // => 'progressive' result.codedWidth; // => 1920 result.codedHeight; // => 1088 result.visibleWidth; // => 1920 result.visibleHeight; // => 1080 result.colorPrimaries; // => 9 (ITU-R BT.2020) result.colorTransfer; // => 18 (ARIB STD-B67 (HLG)) result.colorMatrix; // => 9 (BT2020 Non-constant Luminance) result.colorRangeFull; // => false ``` -------------------------------- ### Initialize Decoder and Load File Source: https://github.com/vanilagy/turbores/blob/main/demo/index.html Sets up the decoder and loads a video file selected by the user. It extracts video tracks, configures the decoder based on the codec, and initializes the canvas and scrubber. ```javascript import { ALL_FORMATS, BlobSource, EncodedPacketSink, Input } from 'mediabunny'; import { Decoder, Frame } from '../src/index'; const fileInput = document.querySelector('#file'); const canvas = document.querySelector('#canvas'); const scrubber = document.querySelector('#scrubber'); const errorField = document.querySelector('#error'); const ctx = canvas.getContext('2d'); const frame = new Frame(); const showError = (error) => { errorField.textContent = String(error); }; let decoder = null; let sink = null; let currentTimestamp = null; let decoding = false; let nextTime = null; fileInput.addEventListener('change', async () => { try { await loadFile(); } catch (error) { showError(error); } }); const loadFile = async () => { const file = fileInput.files[0]; if (!file) { return; } errorField.textContent = ''; // Tear down any previous session await decoder?.close(); // Use Mediabunny to extract packets from the file const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS, }); const videoTrack = await input.getPrimaryVideoTrack(); if (!videoTrack) { throw new Error('The selected file has no video track.'); } // For ISOBMFF files, internalCodecId is the sample entry name, which is the ProRes FourCC const proresFourCc = videoTrack.internalCodecId; decoder = await Decoder.create({ proresFourCc, useSharedMemory: Decoder.canUseSharedMemory(), // Non-Chrome browsers only support I420 allowedOutputFormats: window.chrome ? undefined : ['I420', 'I420A'], }); if (decoder instanceof Error) { throw decoder; } sink = new EncodedPacketSink(videoTrack); canvas.width = videoTrack.displayWidth; canvas.height = videoTrack.displayHeight; currentTimestamp = null; const firstTimestamp = await videoTrack.getFirstTimestamp(); scrubber.min = firstTimestamp; scrubber.max = await videoTrack.computeDuration(); scrubber.step = 'any'; scrubber.value = firstTimestamp; scrubber.disabled = false; showFrame(firstTimestamp); }; ``` -------------------------------- ### Initialize and Load FFmpeg WASM (MT) Source: https://github.com/vanilagy/turbores/blob/main/benchmark/benchmark-ffmpeg-wasm.html Initializes FFmpeg WASM with multi-threading enabled and loads the core. Checks for cross-origin isolation, which is required for MT. ```javascript const { createFFmpeg } = FFmpeg; const logEl = document.getElementById("log"); const runBtn = document.getElementById("run"); const fileInput = document.getElementById("file"); const print = (s) => { logEl.textContent += s + "\n"; }; const ffmpeg = createFFmpeg({ log: true, logger: ({ message }) => print(message), // default @ffmpeg/core is the MULTI-THREADED build corePath: "https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js", }); (async () => { if (!self.crossOriginIsolated) { print("NOT cross-origin isolated -> no SharedArrayBuffer -> MT won't work."); print("serve this page with COOP: same-origin + COEP: require-corp headers."); return; } print("loading ffmpeg core (MT)..."); await ffmpeg.load(); print("ready. pick a file."); runBtn.disabled = false; })(); ``` -------------------------------- ### Run FFmpeg WASM Benchmark Source: https://github.com/vanilagy/turbores/blob/main/benchmark/benchmark-ffmpeg-wasm.html Handles file input and runs the FFmpeg WASM benchmark with specified threads and stream loop counts. Writes the file to the FS, then executes two benchmark runs. ```javascript runBtn.addEventListener("click", async () => { const file = fileInput.files[0]; if (!file) { print("no file selected"); return; } const bytes = new Uint8Array(await file.arrayBuffer()); ffmpeg.FS("writeFile", file.name, bytes); print(`\nrunning on ${file.name} (${bytes.length} bytes)...\n`); const threads = 10; await ffmpeg.run( "-threads", threads.toString(), "-benchmark", "-stream_loop", "0", "-i", file.name, "-f", "null", "-" ); await ffmpeg.run( "-threads", threads.toString(), "-benchmark", "-stream_loop", "9", "-i", file.name, "-f", "null", "-" ); print("\ndone. Check console output."); }); ``` -------------------------------- ### Import TurboRes from local file Source: https://github.com/vanilagy/turbores/blob/main/README.md Import TurboRes directly from a local file path, useful when not using a package manager. ```typescript import { /* ... */ } from './turbores.js'; ``` -------------------------------- ### Seek and Display Video Frame Source: https://github.com/vanilagy/turbores/blob/main/demo/index.html Handles user interaction with the scrubber to seek to a specific time in the video. It decodes the appropriate video frame and draws it onto the canvas, managing decoding state to avoid race conditions. ```javascript scrubber.addEventListener('input', () => { showFrame(Number(scrubber.value)); }); const showFrame = async (time) => { try { if (decoding) { // There's an ongoing decode, so remember it for later instead of decoding it now nextTime = time; return; } decoding = true; // Grab the packet covering this timestamp on demand, and only decode if it's a different one const packet = await sink.getPacket(time); if (packet && packet.timestamp !== currentTimestamp) { currentTimestamp = packet.timestamp; const result = await decoder.decode(packet.data, frame); if (result instanceof Error) { throw result; } const videoFrame = new VideoFrame(result.frameData, { format: result.pixelFormat, codedWidth: result.codedWidth, codedHeight: result.codedHeight, visibleRect: { x: 0, y: 0, width: result.visibleWidth, height: result.visibleHeight, }, colorSpace: { primaries: result.colorPrimariesString ?? null, transfer: result.colorTransferString ?? null, matrix: result.colorMatrixString ?? null, fullRange: result.colorRangeFull, }, timestamp: packet.microsecondTimestamp, duration: packet.microsecondDuration, }); ctx.drawImage(videoFrame, 0, 0); videoFrame.close(); } decoding = false; // Catch up to the most recent time requested while we were busy if (nextTime !== null) { const time = nextTime; nextTime = null; showFrame(time); } } catch (error) { decoding = false; showError(error); } }; ``` -------------------------------- ### Queue Packets for Decoding Source: https://github.com/vanilagy/turbores/blob/main/README.md Queue multiple packets for decoding and await their completion. Decoding jobs resolve in the order they were queued. ```typescript const frame1 = new Frame(); const frame2 = new Frame(); const frame3 = new Frame(); const promise1 = decoder.decode(packetData1, frame1); const promise2 = decoder.decode(packetData2, frame2); const promise3 = decoder.decode(packetData3, frame3); // You can use these to inspect the decoder's decode queue: decoder.decodeQueueSize; // => 3 await decoder.dequeued; decoder.decodeQueueSize; // => 2 await decoder.dequeued; decoder.decodeQueueSize; // => 1 await decoder.dequeued; decoder.decodeQueueSize; // => 0 ``` -------------------------------- ### Create and use a TurboRes Decoder Source: https://github.com/vanilagy/turbores/blob/main/README.md Instantiate a TurboRes decoder for a specific ProRes stream and decode a frame. Ensure to handle potential errors during decoder creation. ```typescript import { Decoder, Frame } from 'turbores'; // Create a decoder for each stream you want to decode const decoder = await Decoder.create({ // The FourCC identifying the ProRes variant, usually found as metadata // within the file that contains the ProRes media. When not available, // 'apch' is a good default. proresFourCc: 'apch', // Whether to use shared-memory multithreading. Recommended for best // performance, but requires cross-origin isolation. useSharedMemory: true, }); if (decoder instanceof Error) { // Handle the error... } // Create a frame to hold the output data const frame = new Frame(); const result = await decoder.decode( packetData, // Uint8Array containing a single ProRes frame frame, ); if (result instanceof Error) { // Handle the error... } ``` -------------------------------- ### TurboRes Benchmark Code Source: https://github.com/vanilagy/turbores/blob/main/benchmark/benchmark-turbores.html This is the main benchmark code. It handles file selection, input processing, decoding, and performance measurement. Use this to test TurboRes decoding speed. ```javascript import { Input, BlobSource, ALL_FORMATS, EncodedPacketSink } from 'mediabunny'; import { Decoder, Frame } from '../src/index'; const p = document.querySelector('p'); document.querySelector('button').addEventListener('click', () => { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.onchange = async (event) => { p.textContent = 'Benchmarking... (may take a while)'; try { const file = event.target.files[0]; const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS, }); const track = await input.getPrimaryVideoTrack(); const sink = new EncodedPacketSink(track); const packets = []; for await (const packet of sink.packets()) { packets.push(packet); } const decoder = await Decoder.create({ proresFourCc: 'apch', useSharedMemory: true, }); const frame = new Frame(); // Warm-up for (let i = 0; i < 3; i++) { for (const packet of packets) { await decoder.decode(packet.data, frame); } } const start = performance.now(); const iters = 2; for (let i = 0; i < iters; i++) { for (const packet of packets) { await decoder.decode(packet.data, frame); } } const elapsed = performance.now() - start; p.textContent = `Done. ${elapsed / iters} ms per full-file decode, ${(iters * packets.length) / (elapsed / 1000)} FPS`; } catch (error) { p.textContent = 'Errored.'; throw error; } }; fileInput.click(); }); ``` -------------------------------- ### Specify Allowed Output Pixel Formats Source: https://github.com/vanilagy/turbores/blob/main/README.md Force the decoder to output frames in a specific pixel format by using the `allowedOutputFormats` option. If the native format is allowed, it will be used. ```typescript const decoder = await Decoder.create({ // This FORCES the decoder to output all frames in I420: allowedOutputFormats: ['I420'], }); ``` ```typescript const decoder = await Decoder.create({ allowedOutputFormats: ['I420', 'I420A', 'I422P10', 'I422AP10'], }); ``` -------------------------------- ### Enable Shared-Memory Multithreading Source: https://github.com/vanilagy/turbores/blob/main/README.md Enable shared-memory multithreading by setting `useSharedMemory` to true. This requires the page to be cross-origin isolated when run in a browser. ```typescript const decoder = await Decoder.create({ useSharedMemory: true, // ... }); ``` -------------------------------- ### Force Synchronous Decoding Source: https://github.com/vanilagy/turbores/blob/main/README.md Force synchronous decoding by setting `concurrency` to zero. Message passing is not required, but this will block the main thread. ```typescript const decoder = await Decoder.create({ useSharedMemory: true, // or false, doesn't matter concurrency: 0, }); ``` -------------------------------- ### Control Thread Concurrency Source: https://github.com/vanilagy/turbores/blob/main/README.md Control the number of threads used for decoding by setting the `concurrency` option. Defaults to `navigator.hardwareConcurrency`. ```typescript const decoder = await Decoder.create({ useSharedMemory: true, concurrency: 8, // Use 8 threads. Defaults to `navigator.hardwareConcurrency`. }); ``` -------------------------------- ### Clean up TurboRes Decoder and Frame Resources Source: https://github.com/vanilagy/turbores/blob/main/README.md Explicitly close decoders and clear frames when they are no longer needed to free up system resources. ```typescript frame.clear(); decoder.close(); ``` -------------------------------- ### HTTP Headers for Cross-Origin Isolation Source: https://github.com/vanilagy/turbores/blob/main/README.md These HTTP response headers are required to enable cross-origin isolation in browsers when using shared-memory multithreading. ```http Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp ``` ```http Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: credentialless ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.