### Start Examples Development Server Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Starts a development server for examples, accessible at http://localhost:5173/examples/[name]/. ```bash npm run dev ``` -------------------------------- ### Build Docs and Examples Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Builds both the project documentation and examples for deployment. ```bash npm run docs:build ``` -------------------------------- ### Mediabunny Configuration Examples Source: https://github.com/vanilagy/mediabunny/blob/main/dev/mux.html Examples of initializing various output formats and targets for media processing. ```javascript const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 480; const context = canvas.getContext('2d'); let format = new Mediabunny.MkvOutputFormat({ streamable: false }); format = new Mediabunny.Mp4OutputFormat({ fastStart: 'fragmented' }); // new Mediabunny.MkvOutputFormat();// new Mediabunny.Mp4OutputFormat({ fastStart: false }); format = new Mediabunny.OggOutputFormat(); format = new Mediabunny.Mp4OutputFormat({ fastStart: 'fragmented', minimumFragmentDuration: 2 }); format = new Mediabunny.MkvOutputFormat({ minimumClusterDuration: 2 }); format = new Mediabunny.WavOutputFormat(); format = new Mediabunny.MkvOutputFormat(); format = new Mediabunny.MovOutputFormat(); format = new Mediabunny.Mp4OutputFormat({ fastStart: 'reserve' }); format = new Mediabunny.WebMOutputFormat(); let target = new Mediabunny.BufferTarget(); /* target = new Mediabunny.StreamTarget(new WritableStream({ async write(chunk) { console.log(chunk) await new Promise(resolve => setTimeout(resolve, 250)); } }), { chunked: true }); */ let output = new Mediabunny.Output({ format, target }); ``` -------------------------------- ### Start Docs Development Server Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Starts a development server for viewing and editing documentation. ```bash npm run docs:dev ``` -------------------------------- ### Install Mediabunny with Bun Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/installation.md Use this command to install Mediabunny via Bun. Ensure you have Bun installed. ```bash bun add mediabunny ``` -------------------------------- ### Install Mediabunny with npm Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/installation.md Use this command to install Mediabunny via npm. Ensure you have Node.js and npm installed. ```bash npm install mediabunny ``` -------------------------------- ### Install Mediabunny and Server Package Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/server.md Install both the core Mediabunny library and the server package using npm. This library peer-depends on Mediabunny. ```bash npm install mediabunny @mediabunny/server ``` -------------------------------- ### Install Mediabunny with Yarn Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/installation.md Use this command to install Mediabunny via Yarn. Ensure you have Yarn installed. ```bash yarn add mediabunny ``` -------------------------------- ### Media Compression Server Example Source: https://github.com/vanilagy/mediabunny/blob/main/packages/server/README.md Example of setting up a Node.js server to compress media files. It streams the request body to Mediabunny for processing and saves the output to disk, achieving O(1) memory usage. ```typescript import { ALL_FORMATS, Conversion, FilePathTarget, Input, Mp4OutputFormat, Output, QUALITY_MEDIUM, ReadableStreamSource } from "mediabunny"; import { registerMediabunnyServer } from "@mediabunny/server"; import { Readable } from "node:stream"; import http from "node:http"; registerMediabunnyServer(); const server = http.createServer(async (req, res) => { // Read the request body as a stream const stream = Readable.toWeb(req) as ReadableStream; const input = new Input({ source: new ReadableStreamSource(stream), formats: ALL_FORMATS, }); // Stream the output directly to the disk, could also stream to S3 etc. const output = new Output({ format: new Mp4OutputFormat(), target: new FilePathTarget(`./converted-${crypto.randomUUID()}.mp4`), }); try { const conversion = await Conversion.init({ input, output, video: async track => ({ codec: 'avc', height: Math.min(720, await track.getDisplayHeight()), bitrate: QUALITY_MEDIUM, }), }); await conversion.execute(); res.statusCode = 204; res.end(); } catch (error) { res.statusCode = 500; res.end(); console.error("Error processing media:", error); } }); server.listen(3000); ``` -------------------------------- ### Example Video Decoder Configuration Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md An example of a `VideoDecoderConfig` object for a 1080p video, including codec, dimensions, and decoder configuration record bytes. ```json { "codec": "avc1.4d4029", "codedWidth": 1920, "codedHeight": 1080, "description": new Uint8Array([ // Bytes of the AVCDecoderConfigurationRecord 1, 77, 64, 41, 255, 225, 0, 22, 39, 77, 64, 41, 169, 24, 15, 0, 68, 252, 184, 3, 80, 16, 16, 27, 108, 43, 94, 247, 192, 64, 1, 0, 4, 40, 222, 9, 200, ]) } ``` -------------------------------- ### Install Dependencies Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Installs project dependencies using npm. ```bash npm install ``` -------------------------------- ### Install Mediabunny with pnpm Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/installation.md Use this command to install Mediabunny via pnpm. Ensure you have pnpm installed. ```bash pnpm add mediabunny ``` -------------------------------- ### Install mediabunny and flac-encoder Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/flac-encoder.md Install the Mediabunny library and the FLAC encoder extension using npm. This is a prerequisite for using the FLAC encoder. ```bash npm install mediabunny @mediabunny/flac-encoder ``` -------------------------------- ### Install mediabunny and mp3-encoder via npm Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/mp3-encoder.md Install the necessary packages using npm. This library peer-depends on Mediabunny. ```bash npm install mediabunny @mediabunny/mp3-encoder ``` -------------------------------- ### Node.js Media Compression Server Example Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/server.md Set up a Node.js server to compress media. It streams the request body to Mediabunny for processing and then streams the output directly to a file. This example demonstrates O(1) memory usage and stream backpressure. ```typescript import { ALL_FORMATS, Conversion, FilePathTarget, Input, Mp4OutputFormat, Output, QUALITY_MEDIUM, ReadableStreamSource } from "mediabunny"; import { registerMediabunnyServer } from "@mediabunny/server"; import { Readable } from "node:stream"; import http from "node:http"; registerMediabunnyServer(); const server = http.createServer(async (req, res) => { // Read the request body as a stream const stream = Readable.toWeb(req) as ReadableStream; const input = new Input({ source: new ReadableStreamSource(stream), formats: ALL_FORMATS, }); // Stream the output directly to the disk, could also stream to S3 etc. const output = new Output({ format: new Mp4OutputFormat(), target: new FilePathTarget(`./converted-${crypto.randomUUID()}.mp4`), }); try { const conversion = await Conversion.init({ input, output, video: async track => ({ codec: 'avc', height: Math.min(720, await track.getDisplayHeight()), bitrate: QUALITY_MEDIUM, }), }); await conversion.execute(); res.statusCode = 204; res.end(); } catch (error) { res.statusCode = 500; res.end(); console.error("Error processing media:", error); } }); server.listen(3000); ``` -------------------------------- ### HLS Program Date Time Example Playlist Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-hls.md Example of a media playlist with `#EXT-X-PROGRAM-DATE-TIME` tags, showing timestamps relative to the Unix epoch. ```m3u8 #EXTM3U #EXT-X-VERSION:3 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-TARGETDURATION:2 #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2, #EXT-X-PROGRAM-DATE-TIME:2024-01-01T00:00:00.000Z segments-1-1.ts #EXTINF:2, #EXT-X-PROGRAM-DATE-TIME:2024-01-01T00:00:02.000Z segments-1-2.ts ... ``` -------------------------------- ### Install mediabunny and aac-encoder Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/aac-encoder.md Install both the mediabunny library and the aac-encoder extension using npm. This is a prerequisite for using the extension. ```bash npm install mediabunny @mediabunny/aac-encoder ``` -------------------------------- ### Install Mediabunny and AC3 Extension Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/ac3.md Install the mediabunny library and the @mediabunny/ac3 package using npm. This is required before using the AC3 extension. ```bash npm install mediabunny @mediabunny/ac3 ``` -------------------------------- ### Example Output MIME Type Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-media-files.md An example of a resolved MIME type string, including video and audio codec information. ```string video/mp4; codecs="avc1.42c032, mp4a.40.2" ``` -------------------------------- ### Starting an Output Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-media-files.md Call `output.start()` after adding all tracks to begin the writing process and prepare the output for media data. This action prevents further track additions. ```typescript await output.start(); // Resolves once the output is ready to receive media data ``` -------------------------------- ### Get First Timestamp Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Retrieve the starting timestamp of the media file in seconds. Useful as media files may not start at time zero. ```typescript await input.getFirstTimestamp(); // => 0.0 ``` -------------------------------- ### Example Audio Decoder Configuration Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md An example of an `AudioDecoderConfig` object for an AAC audio track, including codec, channel count, sample rate, and specific configuration bytes. ```json { "codec": "mp4a.40.2", numberOfChannels: 2, sampleRate: 44100, description: new Uint8Array([ // Bytes of the AudioSpecificConfig 17, 144, ]) } ``` -------------------------------- ### Default Pairing Master Playlist Example Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-hls.md Example of a master playlist generated with default pairing, where audio tracks are grouped into a media rendition group to allow pairing with all video tracks. ```m3u8 #EXTM3U #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-1",NAME="audio-1",URI="audio-1.m3u8" #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-1",NAME="audio-1",URI="audio-2.m3u8" #EXT-X-STREAM-INF:CODECS="avc1.64001f,mp4a.40.2",RESOLUTION=1280x720,AUDIO="audio-1" video-1.m3u8 #EXT-X-STREAM-INF:CODECS="avc1.64001f,mp4a.40.2",RESOLUTION=640x480,AUDIO="audio-1" video-2.m3u8 ``` -------------------------------- ### Initialize HLS Output Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/output-formats.md Example of initializing an Output with the HlsOutputFormat. This sets up the format and target for HLS streaming. ```typescript import { Output, PathedTarget, MpegTsOutputFormat } from 'mediabunny'; const output = new Output({ format: new HlsOutputFormat(options), target: new PathedTarget('master.m3u8', ({ path }) => { // Return a target }), }); ``` -------------------------------- ### Calculate Live Playback Start Time Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-hls.md Calculates a suitable start time for live playback to ensure continuous playback without interruptions. It uses the current duration and refresh interval, with a safety factor. ```typescript const currentDuration = await track.getDurationFromMetadata({ skipLiveWait: true, }); const refreshInterval = await track.getLiveRefreshInterval(); const fac = 2; const playbackStartTime = currentDuration! - fac * refreshInterval!; ``` -------------------------------- ### Basic Audio Conversion Configuration Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md Example of initializing a conversion with specific audio settings, such as converting the audio track to mono and setting a fixed sample rate. ```typescript const conversion = await Conversion.init({ input, output, audio: { numberOfChannels: 1, sampleRate: 48000, }, }); ``` -------------------------------- ### Mediabunny Track Management Source: https://github.com/vanilagy/mediabunny/blob/main/dev/mux.html Examples of adding video, audio, and subtitle tracks to a Mediabunny output instance. ```javascript let videoSource = new Mediabunny.CanvasSource(canvas, { codec: 'vp9', //fullCodecString: 'avc1.42001f', bitrate: 1e6, alpha: 'keep', onEncoderConfig: console.log, }); let audioSource = new Mediabunny.AudioBufferSource({ codec: 'opus', bitrate: 128e3, }); let subtitleSource = new Mediabunny.TextSubtitleSource('webvtt'); output.addVideoTrack(videoSource, { languageCode: 'eng', name: 'Mononoké', maximumPacketCount: 100 }); output.addAudioTrack(audioSource, { name: 'Yooo', maximumPacketCount: 1000 }); //output.addSubtitleTrack(subtitleSource); output.start(); ``` -------------------------------- ### Resize Video Track to 720p Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md Example of initializing a conversion with specific video dimensions and fit mode. This configuration applies to all video tracks in the input. ```typescript const conversion = await Conversion.init({ input, output, video: { width: 1280, height: 720, fit: 'contain', }, }); ``` -------------------------------- ### Register Mediabunny Server Functionality Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/server.md Import and call `registerMediabunnyServer` to enable the full Mediabunny feature set on the server. This is the basic setup required. ```typescript import { registerMediabunnyServer } from '@mediabunny/server'; registerMediabunnyServer(); ``` -------------------------------- ### WritableStream Implementing Backpressure Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-media-files.md Example of a WritableStream that simulates a delay in writing, demonstrating how backpressure can be applied to slow down the data output. ```ts const writable = new WritableStream({ write(chunk: StreamTargetChunk) { // Pretend writing out data takes 10 milliseconds: return new Promise(resolve => setTimeout(resolve, 10)); } }); ``` -------------------------------- ### Get Audio Samples Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves and logs audio samples from the primary audio track. Stops after the first sample. ```javascript const sink = new Mediabunny.AudioSampleSink(await input.getPrimaryAudioTrack()); for await (const sample of sink.samples()) { console.log(sample) break; } ``` -------------------------------- ### Default Track Pairing Example Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-hls.md Demonstrates default track pairing where all tracks are assigned to `Output.defaultTrackGroup`, allowing pairing between different track types. ```typescript output.addVideoTrack(source1); output.addVideoTrack(source2); output.addAudioTrack(source3); output.addAudioTrack(source4); ``` -------------------------------- ### Get Packets within a Time Range Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Demonstrates fetching packets starting from a specific timestamp (5 seconds before the last packet) and logging them. ```javascript /* const lastPacket = await sink.getPacket(Infinity, {skipLiveWait: true}); const other = await sink.getPacket(lastPacket.timestamp - 5); for await (const packet of sink.packets(other)) { console.log(packet.timestamp); } console.log("Done") */ ``` -------------------------------- ### Get All Pairable Audio Tracks and Languages Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-hls.md Fetch all audio tracks that can be paired with the current video track and extract their language codes. ```typescript // Get all audio tracks const availableAudioTracks = await video.getPairableAudioTracks(); const availableLanguages = await Promise.all( availableAudioTracks.map(track => track.getLanguageCode()), ); ``` -------------------------------- ### Get Track First Timestamp Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Retrieves the start timestamp of the first sample in seconds. This value represents when the track's media begins and can be positive, negative, or zero. ```typescript await track.getFirstTimestamp(); // => 0.041666666666666664 ``` -------------------------------- ### Add Packets with Unix Timestamps Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-hls.md When `isRelativeToUnixEpoch` is true, add packets using Unix timestamps. This example shows 10 FPS packets starting on Jan 1, 2024, midnight UTC. ```typescript const source = new EncodedVideoPacketSource('avc'); output.addVideoTrack(source, { isRelativeToUnixEpoch: true }); await output.start(); // 10 FPS starting on 1 Jan 2024, midnight UTC. // The timestamps *are* Unix timestamps: await source.add(new EncodedPacket(data, type, 1704067200.0, 0.1)); await source.add(new EncodedPacket(data, type, 1704067200.1, 0.1)); await source.add(new EncodedPacket(data, type, 1704067200.2, 0.1)); // ... ``` -------------------------------- ### Example: Adding Canvas and Microphone Tracks Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-media-files.md Demonstrates adding a video track from a canvas element and an audio track from the microphone. Ensure correct imports and media source creation. ```typescript import { CanvasSource, MediaStreamAudioTrackSource } from 'mediabunny'; // Assuming `canvasElement` exists const videoSource = new CanvasSource(canvasElement, { codec: 'avc', bitrate: 1e6, // 1 Mbps }); const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const audioStreamTrack = stream.getAudioTracks()[0]; const audioSource = new MediaStreamAudioTrackSource(audioStreamTrack, { codec: 'aac', bitrate: 128e3, // 128 kbps }); output.addVideoTrack(videoSource, { frameRate: 30 }); output.addAudioTrack(audioSource); ``` -------------------------------- ### Initialize and Execute a Media Conversion Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md This snippet demonstrates the basic usage of the `Conversion` class to convert an input media file to WebM format and store the output in a buffer. It includes essential imports and the core steps for initializing and executing a conversion. ```typescript import { Input, Output, WebMOutputFormat, BufferTarget, Conversion, } from 'mediabunny'; const input = new Input({ ... }); const output = new Output({ format: new WebMOutputFormat(), target: new BufferTarget(), }); const conversion = await Conversion.init({ input, output }); if (!conversion.isValid) { // Conversion is invalid and cannot be executed without error. // This field gives reasons for why tracks were discarded: conversion.discardedTracks; // => DiscardedTrack[] return; } await conversion.execute(); // output.target.buffer contains the final file ``` -------------------------------- ### Production Build Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Creates a production-ready build including type definitions. ```bash npm run build ``` -------------------------------- ### Retrieve a single media sample by timestamp Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sinks.md Use `getSample` to retrieve a sample at a specific timestamp. You can also use `Infinity` to get the last sample. ```typescript await sink.getSample(5); // Extracting the first sample: await sink.getSample(await videoTrack.getFirstTimestamp()); // Extracting the last sample: await sink.getSample(Infinity); ``` -------------------------------- ### Iterate Packets and Get Samples Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Iterates through encoded packets, logging each one, and then retrieves the corresponding video sample by timestamp. Continues until the next key packet. ```javascript let packet = await packetSink.getFirstPacket(); while (packet) { console.log(packet) const sample = await sampleSink.getSample(packet.timestamp); packet = await packetSink.getNextKeyPacket(packet); } ``` -------------------------------- ### Negative Trimming for Start Offset Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md Use negative values for the 'start' trim option to introduce silence or a freeze frame at the beginning of the output media. This offsets the media's start time. ```typescript const conversion = await Conversion.init({ // ... trim: { start: -2, // Two seconds of no media data (freeze frame / silence) at the start }, // ... }); ``` -------------------------------- ### Convert input file to FLAC format Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/flac-encoder.md This example demonstrates how to convert an input file to a FLAC file using Mediabunny and the registered FLAC encoder. It first checks for native FLAC support and registers the encoder if necessary, then sets up input and output formats and executes the conversion. ```typescript import { Input, ALL_FORMATS, BlobSource, Output, BufferTarget, FlacOutputFormat, canEncodeAudio, Conversion, } from 'mediabunny'; import { registerFlacEncoder } from '@mediabunny/flac-encoder'; if (!(await canEncodeAudio('flac'))) { registerFlacEncoder(); } const input = new Input({ source: new BlobSource(file), // From a file picker, for example formats: ALL_FORMATS, }); const output = new Output({ format: new FlacOutputFormat(), target: new BufferTarget(), }); const conversion = await Conversion.init({ input, output, }); await conversion.execute(); output.target.buffer; // => ArrayBuffer containing the FLAC file ``` -------------------------------- ### Get Sample for Encoded Packet Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves a specific encoded packet by timestamp and then gets the corresponding video sample. ```javascript const timestamp = 6.666666666666667; const thePacket = await packetSink.getPacket(timestamp); console.log(videoTrack.codec, thePacket, await videoTrack.determinePacketType(thePacket)); sampleSink.getSample(thePacket.timestamp); ``` -------------------------------- ### Convert Files using Mediabunny Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Illustrates how to convert media files from one format to another, for example, to WebM. This involves initializing a `Conversion` object with input and output configurations. ```javascript import { Input, Output, Conversion, ALL_FORMATS, BlobSource, WebMOutputFormat } from 'mediabunny'; const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS, }); const output = new Output({ format: new WebMOutputFormat(), // Convert to WebM target: new BufferTarget(), }); const conversion = await Conversion.init({ input, output }); await conversion.execute(); ``` -------------------------------- ### CustomSource Example: Reading from Disk Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Demonstrates how to use CustomSource to read data from a file using Node.js file system operations. This approach requires custom implementations for read and getSize logic. ```typescript import { CustomSource } from 'mediabunny'; import { open } from 'node:fs/promises'; const fileHandle = await open('bigbuckbunny.mp4', 'r'); const source = new CustomSource({ read: async (start, end) => { const buffer = Buffer.alloc(end - start); await fileHandle.read(buffer, 0, end - start, start); return buffer; }, getSize: async () => { const { size } = await fileHandle.stat(); return size; }, }); ``` -------------------------------- ### Create New Media Files with Mediabunny Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Shows how to create new media files, such as MP4, by adding video tracks from a canvas source and writing to a buffer. Requires importing components like `Output`, `Mp4OutputFormat`, and `CanvasSource`. ```javascript import { Output, Mp4OutputFormat, BufferTarget, CanvasSource, QUALITY_HIGH } from 'mediabunny'; const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget(), // Writing to memory }); // Add a video track backed by a canvas element const videoSource = new CanvasSource(canvas, { codec: 'avc', bitrate: QUALITY_HIGH, }); output.addVideoTrack(videoSource); await output.start(); // Add frames... await output.finalize(); const buffer = output.target.buffer; // Final MP4 file ``` -------------------------------- ### Implement a Media Player with Mediabunny Source: https://github.com/vanilagy/mediabunny/blob/main/dev/player.html This script initializes a media player by handling file input, decoding tracks, and managing playback state through audio and video sinks. It requires a DOM structure with a file input, canvas, and playback controls. ```javascript const fileInput = document.querySelector('input[type="file"]'); const currentTimeElement = document.querySelector('#currentTime'); const durationElement = document.querySelector('#duration'); const playerDiv = document.querySelector('#player'); const playButton = playerDiv.querySelector('button'); const rangeInput = playerDiv.querySelector('input[type="range"]'); const file = await new Promise(resolve => { fileInput.addEventListener('change', () => { resolve(fileInput.files[0]); }); }); fileInput.style.display = 'none'; const audioContext = new AudioContext(); const source = new Mediabunny.BlobSource(file); const input = new Mediabunny.Input({ formats: Mediabunny.ALL_FORMATS, source }); console.log(await input.getMimeType()); let videoTrack = await input.getPrimaryVideoTrack(); let audioTrack = await input.getPrimaryAudioTrack(); if (!(await videoTrack?.canDecode())) { videoTrack = null; } if (!(await audioTrack?.canDecode())) { audioTrack = null; } const canvas = document.querySelector('canvas'); const context = canvas.getContext('2d'); let videoRotation = 0; if (!videoTrack) { canvas.style.display = 'none'; } else { canvas.width = videoTrack.displayWidth; canvas.height = videoTrack.displayHeight; } const totalDuration = await input.computeDuration(); durationElement.textContent = formatSeconds(totalDuration); playerDiv.style.display = 'flex'; const videoSink = videoTrack && new Mediabunny.CanvasSink(videoTrack, { poolSize: 2 }); const audioSink = audioTrack && new Mediabunny.AudioBufferSink(audioTrack); let startTime = null; let playing = false; let playbackTimeAtStart = 0; let videoFrameIterator = null; let audioBufferIterator = null; let currentFrame = null; let nextFrame = null; let seeking = false; let seekId = 0; const queuedAudioNodes = new Set(); function getPlaybackTime() { if (playing) { return audioContext.currentTime - startTime + playbackTimeAtStart; } else { return playbackTimeAtStart; } } function play() { audioBufferIterator?.return(); audioBufferIterator = audioSink?.buffers(getPlaybackTime()); startTime = audioContext.currentTime; playing = true; runAudioIterator(); playButton.textContent = 'Pause'; } function pause() { playbackTimeAtStart = getPlaybackTime(); playing = false; audioBufferIterator?.return(); audioBufferIterator = null; for (const node of queuedAudioNodes) { node.stop(); } playButton.textContent = 'Play'; } function togglePlay() { if (seeking) { return; } if (playing) { pause(); } else { play(); } } async function seek() { if (!videoTrack) { return; } seeking = true; seekId++; await videoFrameIterator?.return(); videoFrameIterator = videoSink.canvases(getPlaybackTime()); const newCurrentFrame = (await videoFrameIterator.next()).value; const newNextFrame = (await videoFrameIterator.next()).value; currentFrame = newCurrentFrame; nextFrame = newNextFrame; if (currentFrame) { context.drawImage(currentFrame.canvas, 0, 0); } seeking = false; } await seek(); async function render() { const playbackTime = getPlaybackTime(); if (playbackTime >= totalDuration) { pause(); playbackTimeAtStart = totalDuration; } if (currentFrame) { while (nextFrame && nextFrame.timestamp <= playbackTime && !seeking) { currentFrame = nextFrame; context.drawImage(currentFrame.canvas, 0, 0); const currentSeekId = seekId; const newNextFrame = (await videoFrameIterator.next()).value; if (currentSeekId === seekId) { nextFrame = newNextFrame; } } } if (!movingRangeInput) { rangeInput.value = playbackTime / totalDuration; currentTimeElement.textContent = formatSeconds(playbackTime); } requestAnimationFrame(render); } async function runAudioIterator() { if (!audioTrack) { return; } for await (let { buffer, timestamp } of audioBufferIterator) { const node = audioContext.createBufferSource(); node.buffer = buffer; node.connect(audioContext.destination); const startTimestamp = startTime + timestamp - playbackTimeAtStart; if (startTimestamp >= audioContext.currentTime) { node.start(startTimestamp); } else { node.start(audioContext.currentTime, audioContext.currentTime - startTimestamp); } queuedAudioNodes.add(node); node.onended = () => { queuedAudioNodes.delete(node); }; if (timestamp - getPlaybackTime() >= 1) { await new Promise(resolve => { const id = setInterval(() => { if (timestamp - getPlaybackTime() < 1) { clearInterval(id); resolve(); } }, 100); }); } } } function formatSeconds(seconds) { seconds = Math.round(seconds * 1000) / 1000; // Round to milliseconds const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}.${Math.floor(1000 * seconds % 1000).toString().padStart(3, '0')}`; } playButton.addEventListener('click', togglePlay); window.addEventListener('keydown', (e) => { if (e.code === 'Space') { togglePlay(); e.preventDefault(); } }); let movingRangeInput = false; rangeInput.addEventListener('input', () => { movingRangeInput = true; const time = Number(rangeInput.value) * totalDurati ``` -------------------------------- ### Iterating Encoded Video Chunks from a Specific Start Chunk Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Iterates through encoded video chunks starting from a specified chunk. This allows for processing a contiguous sequence of chunks. ```javascript const drain = new Mediabunny.EncodedVideoChunkDrain(videoTrack); const startChunk = await drain.getFirstChunk(); for await (const chunk of drain.chunks(startChunk)) { con ``` -------------------------------- ### Create an MP4 File in Memory Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/writing-media-files.md Instantiate an Output object to begin creating an MP4 file. This example configures the output to use the MP4 format and store the resulting file in memory using BufferTarget. ```typescript import { Output, Mp4OutputFormat, BufferTarget } from 'mediabunny'; // In this example, we'll be creating an MP4 file in memory: const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget(), }); ``` -------------------------------- ### Get Track Information and Duration Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves all tracks from a manifest, logs their details, and demonstrates getting the duration from metadata for a specific track. It also shows how to iterate through packets of an audio track. ```javascript /* const tracks = await manifest.getTracks(); console.log(tracks); const [track] = tracks; window.kekw = () => { console.log(track.getDurationFromMetadata()) }; return; const ugh = new Mediabunny.EncodedPacketSink(audioTrack); for await (const packet of ugh.packets()) { console.log(packet); } return; const videoTrack = await manifest.getPrimaryVideoTrack(); await videoTrack.hydrate(); console.log("Doing...") console.log(await videoTrack.getDurationFromMetadata({skipLiveWait: true})); return; const sink = new Mediabunny.EncodedPacketSink(videoTrack); const firstTimestamp = await videoTrack.getFirstTimestamp(); console.log(await videoTrack.computeDuration({ skipLiveWait: true })); manifest.dispose() */ ``` -------------------------------- ### Use ALL_FORMATS Constant Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/input-formats.md Create an Input instance supporting all available formats using the ALL_FORMATS constant. Be aware of potential bundle size increases. ```typescript import { Input, ALL_FORMATS } from 'mediabunny'; const input = new Input({ formats: ALL_FORMATS, // ... }); ``` -------------------------------- ### Iterating Over a Packet Range Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sinks.md Use the `packets` iterator to iterate over a specific range of packets, from a start packet up to (but not including) an end packet. Both start and end can be undefined to signify the beginning or end of the stream. ```typescript const start = await sink.getPacket(5); const end = await sink.getPacket(10, { metadataOnly: true }); for await (const packet of sink.packets(start, end)) { // ... } ``` -------------------------------- ### Convert File to MP3 using Mediabunny Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/mp3-encoder.md Example demonstrating how to convert an input file to an MP3 format using Mediabunny. It includes checking for native support and registering the custom encoder if needed. ```typescript import { Input, ALL_FORMATS, BlobSource, Output, BufferTarget, Mp3OutputFormat, canEncodeAudio, Conversion, } from 'mediabunny'; import { registerMp3Encoder } from '@mediabunny/mp3-encoder'; if (!(await canEncodeAudio('mp3'))) { // Only register the custom encoder if there's no native support registerMp3Encoder(); } const input = new Input({ source: new BlobSource(file), // From a file picker, for example formats: ALL_FORMATS, }); const output = new Output({ format: new Mp3OutputFormat(), target: new BufferTarget(), }); const conversion = await Conversion.init({ input, output, }); await conversion.execute(); output.target.buffer; // => ArrayBuffer containing the MP3 file ``` -------------------------------- ### Monitor Conversion Progress Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md This example shows how to monitor the progress of a media conversion by setting the `onProgress` callback before executing the conversion. The callback receives a progress value between 0 and 1. ```typescript const conversion = await Conversion.init({ input, output }); conversion.onProgress = (progress: number) => { // `progress` is a number between 0 and 1 (inclusive) }; await conversion.execute(); ``` -------------------------------- ### Trim Media Clip by Start and End Times Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/converting-media-files.md Extract a specific section of the input file by defining 'start' and 'end' times in seconds. If only one is provided, the clip extends to the end or beginning of the file, respectively. ```typescript const conversion = await Conversion.init({ input, output, trim: { start: 10, end: 25, }, }); ``` -------------------------------- ### Initialize Player Controls Source: https://github.com/vanilagy/mediabunny/blob/main/dev/player.html Sets up event listeners for player controls, including time updates, seeking via a range input, and pointer release to finalize seeking. It also calls `render` and `play` to initialize the player's state. ```javascript on; currentTimeElement.textContent = formatSeconds(time); }); rangeInput.addEventListener('change', async () => { movingRangeInput = false; if (seeking) { return; } const wasPlaying = playing; if (wasPlaying) { pause(); } const newTime = Number(rangeInput.value) * totalDuration; playbackTimeAtStart = newTime; await seek(); if (wasPlaying) { play(); } }); addEventListener('pointerup', () => movingRangeInput = false); render(); play(); ``` -------------------------------- ### Get Input Format Name Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/input-formats.md Retrieve the full written name of an input format. ```typescript inputFormat.name; ``` -------------------------------- ### Consume Encoded Packets (Simplified) Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html A simplified example of consuming encoded packets from a video track. ```javascript const sink = new Mediabunny.EncodedPacketSink(videoTrack); for await (const packet of sink.packets()) { console.log(packet) } ``` -------------------------------- ### Get Encoded Packet by Timestamp Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves a specific encoded packet by its timestamp, with type verification. ```javascript console.log(await packetSink.getPacket(6.666666666666667, { verifyType: true })) ``` -------------------------------- ### Initialize VideoSampleSource Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sources.md Initializes a VideoSampleSource for encoding and adding video samples. Samples can be optionally forced as key frames. ```typescript import { VideoSampleSource } from 'mediabunny'; const sampleSource = new VideoSampleSource({ codec: 'avc', bitrate: 1e6, }); await sampleSource.add(videoSample); videoSample.close(); // If it's not needed anymore // You may optionally force samples to be encoded as key frames: await sampleSource.add(videoSample, { keyFrame: true }); ``` -------------------------------- ### Get a Specific Encoded Packet Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves a specific encoded packet from an audio track by its index. ```javascript const audioTrack = await input.getPrimaryAudioTrack(); const sink = new Mediabunny.EncodedPacketSink(audioTrack); console.log(await sink.getPacket(100)) ``` -------------------------------- ### Initialize Input and Output for WAV Conversion Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Sets up Mediabunny input from a buffer or blob source and configures an output to convert to WAV format, targeting a buffer. ```javascript const source = new Mediabunny.BufferSource(await file.arrayBuffer()) ?? new Mediabunny.BlobSource(file); const start = performance.now(); const input = new Mediabunny.Input({ formats: Mediabunny.ALL_FORMATS, source }); const output = new Mediabunny.Output({ format: new Mediabunny.WaveOutputFormat(), target: new Mediabunny.BufferTarget() }); output.start(); ``` -------------------------------- ### Get Video Track Timestamps Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves the first timestamp and computes the duration of a video track. ```javascript const videoTrack = await input.getPrimaryVideoTrack(); console.log(await videoTrack.getFirstTimestamp(), await videoTrack.computeDuration()); ``` -------------------------------- ### Get Video Sample Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves a single video sample from a video track using VideoSampleSink. ```javascript const sink = new Mediabunny.VideoSampleSink(videoTrack); const sample = await sink.getSample(0); console.log(sample); ``` -------------------------------- ### Import Input Format Singletons Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/input-formats.md Import all available input format singleton instances from the mediabunny library. ```typescript import { MP4, QTFF, MATROSKA, WEBM, MP3, WAVE, OGG, ADTS, FLAC, MPEG_TS, HLS, } from 'mediabunny'; ``` -------------------------------- ### Get Input Format MIME Type Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/input-formats.md Retrieve the base MIME type of an input format. ```typescript inputFormat.mimeType; ``` -------------------------------- ### Register Mediabunny Server with Hardware Context Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/server.md Optionally configure server-side Mediabunny with specific hardware rendering options. This example shows how to specify a VAAPI hardware context. ```typescript import { registerMediabunnyServer } from '@mediabunny/server'; import * as NodeAv from 'node-av'; registerMediabunnyServer({ // Use a specific hardware rendering device: hardwareContext: NodeAv.HardwareContext.create( NodeAv.AV_HWDEVICE_TYPE_VAAPI, '/dev/dri/renderD128', ), }); ``` -------------------------------- ### Provide Initialization Input for CMAF Files Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Loads a separate initialization file (e.g., 'init.mp4') as an `initInput` to be passed to the main `Input` for formats like CMAF that store track initialization info separately. ```typescript const initInput = new Input({ source: new FilePathSource('init.mp4'), formats: ALL_FORMATS, }); const input = new Input({ source: new FilePathSource('data.mp4'), formats: ALL_FORMATS, initInput, }); ``` -------------------------------- ### QuickTime Output Format Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/output-formats.md Initializes a QuickTime (.mov) output format. This format utilizes the same options as the MP4 format. ```typescript import { Output, MovOutputFormat } from 'mediabunny'; const output = new Output({ format: new MovOutputFormat(options), // ... }); ``` -------------------------------- ### Get MIME Type Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Retrieve the full MIME type of the media file, including track codecs. ```typescript await input.getMimeType(); // => 'video/mp4; codecs="avc1.42c032, mp4a.40.2"' ``` -------------------------------- ### Get Concrete Input Format Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Retrieve the specific format of the media file being read by the Input instance. ```typescript await input.getFormat(); // => Mp4InputFormat ``` -------------------------------- ### Get Metadata Tags Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Read descriptive metadata tags from the media file, such as title, artist, or cover art. ```typescript await input.getMetadataTags(); // => MetadataTags ``` -------------------------------- ### Get a Specific Key Frame Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieve a specific key frame by its timestamp and draw it onto a canvas element. ```javascript const drain = new Mediabunny.VideoFrameDrain(videoTrack); const frame = await drain.getKeyFrame(69); const canvas = document.createElement('canvas') canvas.width = await videoTrack.getWidth(); canvas.height = await videoTrack.getHeight(); const context = canvas.getContext('2d'); context.drawImage(frame, 0, 0); document.body.append(canvas); ``` -------------------------------- ### Create Input with Specific Formats Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Optimize bundle size by specifying only the necessary formats (e.g., MP3, WAVE) when creating an Input instance. ```typescript import { Input, MP3, WAVE } from 'mediabunny'; const input = new Input({ formats: [MP3, WAVE], // .... }); ``` -------------------------------- ### Initialize MediaStreamVideoTrackSource Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sources.md Use this source to pipe real-time video from sources like webcams or screen recordings. Ensure to handle potential errors using errorPromise. ```typescript import { MediaStreamVideoTrackSource } from 'mediabunny'; // Get the user's screen const stream = await navigator.mediaDevices.getDisplayMedia({ video: true }); const videoTrack = stream.getVideoTracks()[0]; const videoTrackSource = new MediaStreamVideoTrackSource(videoTrack, { codec: 'vp9', bitrate: 1e7, }); // Make sure to allow any internal errors to properly bubble up videoTrackSource.errorPromise.catch((error) => ...); ``` -------------------------------- ### Get Primary Audio Track for Video Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-hls.md Retrieve the primary audio track that pairs with a given video track. ```typescript // Get the audio track that accompanies this video track const matchingAudioTrack = await video.getPrimaryPairableAudioTrack(); ``` -------------------------------- ### Convert NodeAV Frames to Mediabunny Samples Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/server.md Demonstrates creating Mediabunny VideoSample and AudioSample instances directly from NodeAV Frame objects without data copying. Also shows how to convert Mediabunny samples back to NodeAV Frames. ```typescript import { VideoSample, AudioSample } from 'mediabunny'; import { AvFrameVideoSampleResource, AvFrameAudioSampleResource, toAvFrame } from '@mediabunny/server'; // Frame -> VideoSample new VideoSample(new AvFrameVideoSampleResource(frame), { timestamp }); // Frame -> AudioSample new AudioSample(new AvFrameAudioSampleResource(frame)); // (uses the timestamp in the frame) // VideoSample -> Frame await toAvFrame(videoSample, frame); // AudioSample -> Frame await toAvFrame(audioSample, frame); ``` -------------------------------- ### Register MP3 Encoder Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/extensions/mp3-encoder.md Register the MP3 encoder with Mediabunny. This is the basic setup required to enable MP3 encoding. ```typescript import { registerMp3Encoder } from '@mediabunny/mp3-encoder'; registerMp3Encoder(); ``` -------------------------------- ### Basic Media Source Addition Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sources.md This is the general pattern for adding media data using most media sources. ```typescript await mediaSource.add(...); ``` -------------------------------- ### Read File Metadata with Mediabunny Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Demonstrates how to read metadata such as duration, video/audio tracks, display dimensions, and tags from a media file. Requires importing necessary components from 'mediabunny'. ```javascript import { Input, ALL_FORMATS, BlobSource } from 'mediabunny'; // Reading from disk const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS, }); const duration = await input.computeDuration(); // in seconds const videoTrack = await input.getPrimaryVideoTrack(); const audioTrack = await input.getPrimaryAudioTrack(); const displayWidth = await videoTrack.getDisplayWidth(); const displayHeight = await videoTrack.getDisplayHeight(); const rotation = await videoTrack.getRotation(); const sampleRate = await audioTrack.getSampleRate(); const numberOfChannels = await audioTrack.getNumberOfChannels(); const { title, artist, album } = await input.getMetadataTags(); ``` -------------------------------- ### Get All Media Tracks Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Retrieve a list of all media tracks present in the input file. This is the most basic method for track extraction. ```typescript await input.getTracks(); // => InputTrack[] ``` -------------------------------- ### Get Duration from Metadata Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-media-files.md Attempt to retrieve the media file's duration directly from its metadata. Returns null if not available. ```typescript await input.getDurationFromMetadata(); // => number | null ``` -------------------------------- ### Generate API Docs Source: https://github.com/vanilagy/mediabunny/blob/main/README.md Generates API documentation for the project. ```bash npm run docs:generate ``` -------------------------------- ### Naive implementation for retrieving key frame samples Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/media-sinks.md This naive implementation iterates over all key packets first, then retrieves samples, which is less efficient. ```typescript // Naive, bad implementation: // [!code error] const packetSink = new EncodedPacketSink(videoTrack); const keyFrameTimestamps: number[] = []; let currentPacket = await packetSink.getFirstPacket(); while (currentPacket) { keyFrameTimestamps.push(currentPacket.timestamp); currentPacket = await packetSink.getNextKeyPacket(currentPacket); } const sampleSink = new VideoSampleSink(videoTrack); const keyFrameSamples = sampleSink.samplesAtTimestamps(keyFrameTimestamps); for await (const sample of keyFrameSamples) { // ... sample.close(); } ``` -------------------------------- ### Processing Audio Buffers Source: https://github.com/vanilagy/mediabunny/blob/main/dev/demux.html Retrieves audio buffers from an audio track. The snippet shows how to get the next buffer from the drain. ```javascript const drain = new Mediabunny.AudioBufferDrain(audioTrack); const chunks = drain.buffers() const a = chunks.next(); ``` -------------------------------- ### Get Specific Language Audio Track Source: https://github.com/vanilagy/mediabunny/blob/main/docs/guide/reading-hls.md Find the primary audio track for a specific language, in this case, Spanish ('es'). ```typescript // Get the Spanish audio track const matchingSpanishAudio = await video.getPrimaryPairableAudioTrack({ filter: async track => await track.getLanguageCode() === 'es', }); ```