UNPKG

ipfs-hls-player

Version:

Video.js-based HLS player optimized for IPFS content-addressed storage

597 lines (456 loc) 21.6 kB
# IPFS HLS Player **Version 1.2.3** A complete video player for IPFS-hosted content. This all-in-one solution handles HLS streams, MP4, MOV, WebM, and other formats with automatic enhancement, seamless loading, and intelligent fallback to native players when optimal. **Universal Format Support** - HLS, MP4, MOV, WebM, and more **Automatic Type Detection** - Works with extensionless IPFS CIDs **MOV File Compatibility** - Full Chrome/Brave support via intelligent MIME normalization **Seamless Loading** - No flash or layout shift **Cross-Browser** - Works everywhere, including Safari **Zero Configuration** - Just add to your page and it works **Production Ready** - Used in real IPFS applications ## What This Does This player solves the fundamental challenges of serving video from IPFS: 1. **HLS Compatibility**: Handles IPFS URLs in HLS playlists 2. **Type Detection**: Identifies formats without file extensions 3. **Safari Support**: Automatically uses native player when optimal 4. **Professional UX**: Smooth loading with no visual glitches ## The IPFS HLS Challenge Standard HLS uses relative paths in playlists: ```m3u8 #EXTINF:10.0, segment001.ts ``` IPFS-compatible HLS uses absolute URLs with CIDs (each segment is a separate IPFS object): ```m3u8 #EXTINF:10.0, https://gateway.ipfs.io/ipfs/QmSegmentHash123 ``` **How HLS works on SPK Network:** During the transcoding/upload process: 1. Each video segment (.ts file) gets its own CID 2. The M3U8 playlist is rewritten to replace relative paths with absolute IPFS gateway URLs 3. The modified playlist gets its own CID 4. All segments and playlists are uploaded to IPFS 5. You play the video using the playlist's CID: `https://gateway.ipfs.io/ipfs/QmPlaylistHash` This player is optimized to play these IPFS-hosted HLS streams. ## What This Player Provides - **Pre-configured Video.js**: Optimized settings for IPFS gateway playback - **VHS Override**: Forces JavaScript HLS implementation for better compatibility - **Quality Selection UI**: Built-in adaptive bitrate controls - **Automatic Enhancement**: Finds and upgrades video elements on the page - **IPFS Gateway Optimization**: Tuned for distributed gateway performance - **Intelligent Type Detection**: Handles IPFS CIDs and various video formats without assumptions ## Features - **IPFS-Optimized**: Pre-configured for IPFS-hosted HLS streams - **Quality Selector**: Manual quality selection UI for HLS streams (Chrome/Firefox) - **Auto-Enhancement**: Automatically upgrades video elements - **Seamless Loading**: No flash or layout shift during initialization - **Universal Support**: Works with HLS, MP4, MOV, WebM across all browsers - **Enhanced MOV Support**: Chrome/Brave compatibility via MIME type normalization (v1.2.3) - **Intelligent Detection**: Automatic format detection via magic bytes and filename parameters - **Native Fallback**: Seamlessly uses native players when optimal - **Responsive**: Mobile-friendly controls and layouts - **Customizable**: Clean styling with CSS variables - **Vue Integration**: Optional Vue.js mixin included ## Installation ### NPM ```bash npm install ipfs-hls-player ``` ### CDN ```html <!-- CSS --> <link rel="stylesheet" href="https://unpkg.com/ipfs-hls-player/css/ipfs-hls-player.css"> <!-- JavaScript (minified for production) --> <script src="https://unpkg.com/ipfs-hls-player/dist/ipfs-hls-player.min.js"></script> ``` ## Usage ### Basic Setup ```html <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="path/to/ipfs-hls-player.css"> </head> <body> <!-- Apply width constraints to container, not video element --> <div style="max-width: 800px; margin: 0 auto;"> <video id="my-video" style="width: 100%;"></video> </div> <script src="path/to/ipfs-hls-player.js"></script> <script> // Initialize player with IPFS HLS stream const video = document.getElementById('my-video'); IPFSHLSPlayer.initializePlayer(video, { src: 'https://ipfs.io/ipfs/QmYourPlaylistCID' }); </script> </body> </html> ``` **Important:** Apply width constraints (max-width, width) to the container element, not the video element itself. This ensures proper sizing in both normal and fullscreen modes. ### Automatic Enhancement The player can automatically enhance all video elements on a page: ```javascript // Enhance all videos on page load document.addEventListener('DOMContentLoaded', async () => { await IPFSHLSPlayer.enhanceStaticVideos(); }); ``` ### Vue.js Integration ```javascript import IPFSHLSPlayerMixin from 'ipfs-hls-player/src/vue-integration'; export default { mixins: [IPFSHLSPlayerMixin], // Your component code... } ``` ### CSS Class Configuration The player allows customization of which CSS classes are applied to video elements: ```javascript // Customize CSS classes for specific styling needs IPFSHLSPlayer.initializePlayer(video, { src: 'https://ipfs.io/ipfs/QmYourPlaylistCID', cssClasses: { // Required classes (always applied) required: ['video-js', 'vjs-default-skin'], // Optional classes (applied by default, can be overridden) optional: ['vjs-big-play-centered', 'vjs-fluid'], // Custom classes for your application custom: ['my-custom-class', 'theme-dark'] } }); ``` This is useful when: - Integrating with existing styling systems - Preventing conflicts with other video frameworks - Applying custom themes or layouts ### Intelligent MIME Type Detection The player includes advanced type detection specifically designed for IPFS content where file extensions are not available: #### Magic Byte Detection For IPFS CIDs without file extensions, the player uses **magic byte detection** - the industry standard method for identifying file types: 1. **Fetches first 200 bytes** of the content using a Range request (efficient) 2. **Checks magic bytes** to identify the format: - `#EXTM3U` → HLS playlist (`application/x-mpegURL`) - `ftyp` box → MP4/MOV/QuickTime (`video/mp4`) - EBML header → WebM/Matroska (`video/webm`) - `OggS` → Ogg/Ogv (`video/ogg`) - And more formats... 3. **Falls back to Content-Type** header if magic bytes don't match 4. **Normalizes MIME types** (e.g., `video/quicktime``video/mp4`) #### Video.js Middleware Integration The player uses Video.js middleware to transparently handle IPFS URLs: ```javascript // Automatically detects type for IPFS URLs IPFSHLSPlayer.initializePlayer(video, { src: 'https://ipfs.io/ipfs/QmYourCID' // No type needed! }); ``` The middleware: - Only processes IPFS URLs without an explicit type - Runs magic byte detection asynchronously - Provides the detected type to Video.js - Falls back to `video/mp4` if detection fails #### Manual Type Override You can still manually specify types for faster initialization: ```javascript IPFSHLSPlayer.initializePlayer(video, { src: 'https://ipfs.io/ipfs/QmYourHLSPlaylist', type: 'application/x-mpegURL' // Skip detection for known HLS }); ``` This intelligent detection ensures all video formats work correctly with IPFS, not just MP4. ## API Reference ### `IPFSHLSPlayer.initializePlayer(element, options)` Initialize a player on a video element. **Parameters:** - `element` (HTMLVideoElement): Video element to enhance - `options` (Object): Player configuration - `src` (String): Video source URL - `type` (String): MIME type (optional - intelligently auto-detected based on URL) - `poster` (String): Poster image URL - `autoplay` (Boolean): Auto-start playback - `loop` (Boolean): Loop playback - `muted` (Boolean): Start muted - `cssClasses` (Object): CSS class configuration - `required` (Array): Classes always applied - `optional` (Array): Default classes (can be overridden) - `custom` (Array): Additional custom classes **Returns:** Video.js player instance ### `IPFSHLSPlayer.enhanceVideoElement(video, options)` Enhance an existing video element with IPFS HLS Player. **Parameters:** - `video` (HTMLVideoElement): Video element to enhance - `options` (Object): Enhancement options **Returns:** Promise<Player> ### `IPFSHLSPlayer.destroyPlayer(element)` Destroy a player instance and clean up. **Parameters:** - `element` (HTMLVideoElement): Video element with player ### `IPFSHLSPlayer.enhanceStaticVideos(container)` Enhance all unenhanced videos in a container. **Parameters:** - `container` (HTMLElement): Container to search within (default: document) **Returns:** Promise<Array> of player instances ## Seamless Loading Experience The player provides a completely seamless initialization with no visual jumps or layout shifts: ### How It Works 1. **Preemptive Sizing**: CSS immediately establishes 16:9 aspect ratio for all videos 2. **Hidden Initial State**: Videos are hidden while enhancement occurs 3. **Loading Indicator**: Spinner shows during player initialization 4. **Smooth Reveal**: Videos fade in when ready ### Loading States - **`ipfs-video-loading`**: Shows spinner, hides video - **`ipfs-video-ready`**: Video enhanced and ready, fades in smoothly - **`ipfs-video-error`**: Fallback state if enhancement fails ### CSS Requirements **Important**: Don't apply generic `video` styles in your CSS. The player handles all sizing: ```css /* DON'T do this */ video { max-width: 100%; height: auto; } /* DO this - containers need explicit dimensions */ .my-video-container { max-width: 800px; width: 100%; /* Important for Safari */ aspect-ratio: 16/9; /* Prevents fullscreen issues */ margin: 0 auto; } ``` **Safari Note**: Containers must have explicit `width` and `aspect-ratio` to prevent fullscreen behavior when creating videos dynamically. ### Benefits - **Zero layout shift** - Space reserved from first paint - **No flash of unstyled content** - Videos hidden until ready - **Professional appearance** - Smooth transitions and loading feedback - **Consistent sizing** - 16:9 aspect ratio maintained throughout ## Philosophy: Capability-Based with Browser-Specific Workarounds This player primarily uses capability detection but includes specific browser detection where necessary to work around known limitations. This pragmatic approach ensures reliable playback across all browsers. ### How It Works Player selection is determined by: 1. **Native HLS Support**: Can the browser play HLS natively? 2. **MediaSource Extensions (MSE)**: Does the browser support MSE for JavaScript-based playback? 3. **Browser-Specific Limitations**: Are there known issues that require browser-specific handling? - Safari/WebKit browsers are explicitly detected due to Video.js 8.x limitations - The detection is scalable to add other browsers if similar issues arise ### Self-Contained Type Detection The player is fully self-contained and can intelligently detect video types without requiring type attributes. While type attributes are respected if provided (for performance), the player can determine the correct type through multiple methods: #### Detection Hierarchy (in order of priority) 1. **Explicit Type** (fastest) ```html <video src="video.m3u8" type="application/x-mpegURL"> ``` If provided, the type is used immediately - no detection needed. 2. **URL Query Parameters** ``` https://example.com/video?type=video/mp4 https://ipfs.gateway.com/ipfs/QmCID?filename=video.mov ``` Type hints embedded in the URL are extracted and used. Supports both `type` and `filename` parameters (v1.2.3+). 3. **File Extensions** ``` .m3u8 → application/x-mpegURL .mp4 → video/mp4 .webm → video/webm .mov → video/mp4 (normalized for Chrome/Brave compatibility) ``` Common extensions are mapped to MIME types. MOV files are normalized to `video/mp4` to ensure Chrome/Brave compatibility (v1.2.3+). 4. **Magic Byte Detection** (universal fallback) ``` #EXTM3U → HLS Playlist ftyp box → MP4/MOV/QuickTime EBML header → WebM/Matroska OggS → Ogg Video ``` For extensionless URLs (like IPFS CIDs), the player fetches the first 200 bytes and identifies the format by its binary signature. #### Key Points - **Type is optional**: The player works perfectly without type attributes - **Type is respected**: If you provide a type, it's used (no detection overhead) - **Universal compatibility**: Works with any URL format - extensions, extensionless, or query params - **Performance optimized**: Detection only runs when needed - **Native player support**: Both native and Video.js players benefit from type detection ### Player Strategy ```javascript // Decision tree (no browser names, just capabilities): if (content is HLS) { if (has native HLS && cannot override) → Use native player else if (has MSE) → Use Video.js with HLS else if (has native HLS) → Use native player else → Try Video.js anyway } else { → Use Video.js (handles all other formats) } ``` ### Browser Compatibility Notes #### Safari/WebKit Special Handling Safari on macOS/iOS has native HLS support that cannot be reliably overridden by Video.js 8.x. The player explicitly detects Safari browsers using WebKit-specific properties and automatically uses the native player for HLS content. **Detection Method:** - Checks for WebKit-specific APIs (`webkitEnterFullscreen`, `webkitSupportsFullscreen`) - Verifies Safari-specific properties (`window.safari` object) - Excludes Chrome (which also has some WebKit properties but works fine with Video.js) **Why Special Handling?** This is a known limitation of Video.js 8.x with Safari. Rather than attempting to override Safari's native HLS (which causes playback issues), the player intelligently uses Safari's native capabilities. #### Why Native Players Are Sometimes Used When Video.js 8.x cannot properly override a browser's native HLS implementation (a technical limitation of Video.js), the player automatically uses native playback. This ensures the best possible user experience regardless of browser. ## Configuration ### Global Configuration ```javascript window.ipfsHLSPlayerConfig = { debug: true, // Enable debug logging enableStaticEnhancement: false // Disable automatic enhancement }; ``` ### Player Options ```javascript IPFSHLSPlayer.initializePlayer(video, { html5: { vhs: { overrideNative: true, // Critical for IPFS smoothQualityChange: true, fastQualityChange: true } }, playbackRates: [0.5, 1, 1.5, 2], fluid: true, responsive: true }); ``` ## IPFS Gateway Support The player works with any IPFS gateway: ```javascript // IPFS.io gateway src: 'https://ipfs.io/ipfs/QmPlaylistCID' // Cloudflare gateway src: 'https://cloudflare-ipfs.com/ipfs/QmPlaylistCID' // Local IPFS node src: 'http://localhost:8080/ipfs/QmPlaylistCID' // Custom gateway (like SPK Network) src: 'https://ipfs.dlux.io/ipfs/QmPlaylistCID' ``` ## HLS Quality Selector The player includes an optimized quality selector for HLS streams with proper timing to ensure all quality levels are detected: ### How It Works 1. **For .m3u8 URLs**: Quality selector initializes immediately 2. **For IPFS CIDs**: - Waits for the `loadstart` event (after type detection) - Checks if content is HLS - Initializes selector before manifest loads - Ensures all quality levels are tracked ### Quality Levels Display - Shows all available resolutions (1080p, 720p, 480p, etc.) - Allows manual quality selection - Displays current quality - Only appears for HLS content (hidden for MP4s) ### Timing Optimization The player uses the `loadstart` event for perfect timing: - Fires after middleware detects content type - Occurs before HLS manifest loads - Ensures quality selector catches all levels as they're added - Prevents empty quality dropdowns ## Browser Support - Chrome 60+ (MOV files fully supported via MIME normalization) - Firefox 55+ - Safari 11+ (HLS via native player) - Edge 79+ - Brave (MOV files fully supported via MIME normalization) - iOS Safari 11+ (HLS via native player) - Chrome for Android ### Safari & Native HLS Support The player automatically detects browser capabilities and selects the best playback method: #### Safari Native Player Approach -**All video formats** - Safari uses native player for everything (HLS, MP4, MOV, WebM) -**Consistent user experience** - Same controls for all video types -**Better performance** - Native player is more efficient -**Safari/iOS features** - Full support for AirPlay, Picture-in-Picture, media keys - ⚠️ **Limited customization** - No quality selector or custom controls #### Why Safari Uses Native for Everything 1. **Consistency**: Users see the same familiar Safari controls for all videos 2. **Performance**: Native player provides better battery life and smoother playback 3. **Integration**: Seamless support for Safari-specific features like AirPlay 4. **Simplicity**: Cleaner code path with fewer compatibility issues #### Feature Comparison | Feature | Chrome/Firefox (Video.js) | Safari (Native) | |---------|--------------------------|-----------------| | HLS Playback | ✅ | ✅ | | MP4/MOV/WebM | ✅ | ✅ | | Quality Selector | ✅ | ❌ (HLS auto) | | Custom Controls | ✅ | ❌ | | AirPlay Support | ❌ | ✅ | | Picture-in-Picture | ✅ | ✅ | | Adaptive Bitrate | ✅ | ✅ (automatic) | | Type Detection | ✅ | ✅ | #### Technical Background Safari's native video player provides the best experience for Safari users. Rather than forcing Video.js on Safari (which has compatibility issues with HLS), the player uses Safari's native capabilities for all content types. This design choice prioritizes user experience, performance, and reliability over feature parity across browsers. ## Troubleshooting ### CORS Errors Ensure your IPFS gateway supports CORS headers for cross-origin playback. Most public gateways handle this correctly, but local nodes may need configuration. ### Mixed Content Warnings If your site uses HTTPS, ensure all IPFS gateway URLs also use HTTPS. Browsers block HTTP content on HTTPS pages. ### Player Not Initializing - Check browser console for errors - Verify Video.js CSS is loading (the player includes automatic fallback CDN loading) - Ensure the video element exists in DOM before calling `initializePlayer` ### HLS Playback Issues - Verify the M3U8 playlist uses absolute IPFS URLs (not relative paths) - Check that all segment files are accessible via the gateway - Test the playlist URL directly in the browser ### Type Detection Issues If videos fail to play with IPFS CIDs: - Check the Content-Type headers from your IPFS gateway using browser dev tools - Try manually specifying the type parameter - Ensure your gateway properly sets Content-Type headers for video files - For HLS streams without .m3u8 extension, always specify `type: 'application/x-mpegURL'` ### Safari HLS Playback HLS streams work automatically in Safari using the native player. If you encounter issues with HLS in Safari: - Verify the HLS playlist uses absolute IPFS URLs - Check that segments are accessible via the gateway - Native player controls will be used (quality selector unavailable) - Test in Chrome or Firefox to compare Video.js features ### "Player Already Initialized" Warning When reusing video elements: ```javascript // Clean up existing player first if (video._ipfsHLSPlayer) { IPFSHLSPlayer.destroyPlayer(video); } // Now safe to initialize new player const player = IPFSHLSPlayer.initializePlayer(video, options); ``` ## Development ```bash # Install dependencies npm install # Development build with watch npm run dev # Production build npm run build ``` **Note:** Browsers handle local file access differently. For testing the examples, it's recommended to serve them from a local web server (e.g., `python -m http.server` or `npx http-server`). ## Why Use This Player While Video.js can play HLS streams out of the box, IPFS-hosted content benefits from specific optimizations: 1. **Gateway Performance**: Tuned buffering and timeout settings for distributed gateways 2. **JavaScript HLS**: Uses VHS (Video.js HTTP Streaming) which handles IPFS URLs more reliably than native HLS 3. **Quality Selection**: Clear UI for manual quality switching when adaptive bitrate needs help 4. **Simplified Setup**: Pre-configured Video.js with all the right settings for IPFS content This player packages these optimizations into a drop-in solution for IPFS video playback. ## Changelog ### Version 1.2.3 (Latest) - **Fixed**: MOV files now play correctly in Chrome/Brave browsers - **Added**: Filename parameter detection for SPK Drive compatibility - **Improved**: MIME type normalization (MOV → MP4) for broader browser support - **Enhanced**: Type detection now checks `?filename=` URL parameters ### Version 1.2.2 - Safari native player strategy for all video types - Improved poster frame support for Safari ### Version 1.2.0 - Initial stable release with full IPFS HLS support - Magic byte detection for extensionless URLs - Video.js middleware integration ## Credits Built on top of: - [Video.js](https://videojs.com/) - The open source HTML5 video player - [videojs-hls-quality-selector](https://github.com/chrisboustead/videojs-hls-quality-selector) - Quality selection UI ## License MIT © Mark E. Giles See [LICENSE](LICENSE) file for details.