MediaFox
A framework-agnostic, TypeScript-first Media Player library powered by Mediabunny. MediaFox provides an ergonomic API for media playback with complete control over rendering and UI.
Features
- Framework Agnostic - Works with React, Vue, Angular, or vanilla JavaScript
- Complete Media Support - Video, audio, and subtitle tracks
- Performance First - Efficient frame buffering and audio scheduling
- Small & Tree-shakable - Only include what you use
- UI Flexibility - You control the interface completely
- TypeScript Native - Full type safety and excellent IDE support
- Advanced Features - Screenshot, frame extraction, quality switching
- Plugin System - Extend functionality easily
Installation
bun add @mediafox/core mediabunny
npm install @mediafox/core mediabunny
yarn add @mediafox/core mediabunny
Note: Mediabunny is a peer dependency. You need to install it alongside @mediafox/core.
Quick Start
import { MediaFox } from '@mediafox/core';
const player = new MediaFox({
renderTarget: document.querySelector('canvas'),
volume: 0.8
});
await player.load(videoFile);
await player.play();
player.pause();
player.currentTime = 30;
player.volume = 0.5;
player.subscribe(state => {
console.log(`Time: ${state.currentTime}/${state.duration}`);
console.log(`State: ${state.state}`);
});
player.on('play', () => console.log('Playing'));
player.on('pause', () => console.log('Paused'));
player.on('timeupdate', ({ currentTime }) => {
updateProgressBar(currentTime);
});
Framework Packages
For React applications, use the dedicated React package:
npm install @mediafox/react @mediafox/core mediabunny
import { useMediaFox } from '@mediafox/react';
function VideoPlayer({ src }: { src: File | string }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const { player, state, play, pause } = useMediaFox({
renderTarget: canvasRef.current,
onError: (error) => console.error(error)
});
}
Framework Integration Examples
React (using core package)
import { useEffect, useRef, useState } from 'react';
import { MediaFox, type PlayerStateData } from '@mediafox/core';
function VideoPlayer({ src }: { src: File | string }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const playerRef = useRef<MediaFox>();
const [state, setState] = useState<PlayerStateData>();
useEffect(() => {
const player = new MediaFox({
renderTarget: canvasRef.current!
});
playerRef.current = player;
const subscription = player.subscribe(setState);
player.load(src);
return () => {
subscription.unsubscribe();
player.dispose();
};
}, [src]);
return (
<div>
<canvas ref={canvasRef} />
<div>
<button onClick={() => playerRef.current?.play()}>Play</button>
<button onClick={() => playerRef.current?.pause()}>Pause</button>
<div>{state?.currentTime} / {state?.duration}</div>
</div>
</div>
);
}
Vue 3
<template>
<div>
<canvas ref="canvasRef" />
<div>
<button @click="play">Play</button>
<button @click="pause">Pause</button>
<div>{{ currentTime }} / {{ duration }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { MediaFox } from '@mediafox/core';
const props = defineProps<{ src: File | string }>();
const canvasRef = ref<HTMLCanvasElement>();
const player = ref<MediaFox>();
const currentTime = ref(0);
const duration = ref(0);
onMounted(async () => {
player.value = new MediaFox({
renderTarget: canvasRef.value
});
player.value.subscribe(state => {
currentTime.value = state.currentTime;
duration.value = state.duration;
});
await player.value.load(props.src);
});
onUnmounted(() => {
player.value?.dispose();
});
const play = () => player.value?.play();
const pause = () => player.value?.pause();
</script>
Advanced Usage
Track Management
const videoTracks = player.getVideoTracks();
const audioTracks = player.getAudioTracks();
await player.selectVideoTrack(videoTracks[0].id);
await player.selectAudioTrack(audioTracks[1].id);
videoTracks.forEach(track => {
console.log(`${track.codec} ${track.width}x${track.height} @${track.frameRate}fps`);
});
const blob = await player.screenshot({
format: 'png',
quality: 0.9
});
player.currentTime = 10.5;
const frameBlob = await player.screenshot();
Event Handling
player.on('loadedmetadata', (info) => {
console.log(`Duration: ${info.duration}`);
console.log(`Format: ${info.format}`);
console.log(`Has video: ${info.hasVideo}`);
console.log(`Has audio: ${info.hasAudio}`);
});
player.on('error', (error) => {
console.error('Playback error:', error);
});
player.on('trackchange', ({ type, trackId }) => {
console.log(`${type} track changed to ${trackId}`);
});
Custom Rendering
const canvas = document.createElement('canvas');
player.setRenderTarget(canvas);
const offscreen = canvas.transferControlToOffscreen();
player.setRenderTarget(offscreen);
API Reference
Constructor Options
interface PlayerOptions {
renderTarget?: HTMLCanvasElement | OffscreenCanvas;
audioContext?: AudioContext;
volume?: number;
muted?: boolean;
playbackRate?: number;
autoplay?: boolean;
preload?: 'none' | 'metadata' | 'auto';
crossOrigin?: string;
maxCacheSize?: number;
}
Main Methods
load(source: MediaSource, options?: LoadOptions): Promise<void> - Load media file
play(): Promise<void> - Start playback
pause(): void - Pause playback
seek(time: number): Promise<void> - Seek to time
stop(): void - Stop playback and reset
screenshot(options?: ScreenshotOptions): Promise<Blob> - Take screenshot
dispose(): void - Clean up resources
destroy(): void - Destroy player completely
Properties
currentTime: number - Current playback position
duration: number - Total duration (readonly)
volume: number - Volume level (0-1)
muted: boolean - Mute state
playbackRate: number - Playback speed
paused: boolean - Pause state (readonly)
ended: boolean - Ended state (readonly)
seeking: boolean - Seeking state (readonly)
State Management
const subscription = player.subscribe(state => {
});
const state = player.getState();
subscription.unsubscribe();
Events
All events follow the pattern:
player.on(eventName, callback);
player.once(eventName, callback);
player.off(eventName, callback);
Available events:
loadstart, loadedmetadata, loadeddata, canplay, canplaythrough
play, pause, playing, ended
timeupdate, durationchange, volumechange, ratechange
seeking, seeked, waiting, progress
error, trackchange, qualitychange, resize
Browser Support
MediaFox requires a modern browser with support for:
- WebCodecs API
- Web Audio API
- Canvas API
- ES2022+
License
MIT
Credits
Powered by Mediabunny - the powerful media processing library for the web.