Skip to content

markegiles/ipfs-hls-player

Repository files navigation

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:

#EXTINF:10.0,
segment001.ts

IPFS-compatible HLS uses absolute URLs with CIDs (each segment is a separate IPFS object):

#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

npm install ipfs-hls-player

CDN

<!-- 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

<!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:

// Enhance all videos on page load
document.addEventListener('DOMContentLoaded', async () => {
  await IPFSHLSPlayer.enhanceStaticVideos();
});

Vue.js Integration

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:

// 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/quicktimevideo/mp4)

Video.js Middleware Integration

The player uses Video.js middleware to transparently handle IPFS URLs:

// 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:

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

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 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:

/* 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)

    <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

// 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

window.ipfsHLSPlayerConfig = {
  debug: true,                      // Enable debug logging
  enableStaticEnhancement: false    // Disable automatic enhancement
};

Player Options

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:

// 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:

// Clean up existing player first
if (video._ipfsHLSPlayer) {
  IPFSHLSPlayer.destroyPlayer(video);
}
// Now safe to initialize new player
const player = IPFSHLSPlayer.initializePlayer(video, options);

Development

# 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:

License

MIT © Mark E. Giles

See LICENSE file for details.

About

A zero-config, standalone Video.js-based player service that automatically upgrades <video> elements for seamless HLS playback from IPFS. Designed for use on DLUX with SPK Network, where CID-based .m3u8 playlists enable reliable streaming. Features quality selection and full player lifecycle management.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors