Skip to main content

Video Playback

Video Playback API

HesedVid delivers video content through a global CDN using HLS (HTTP Live Streaming) with adaptive bitrate streaming. Our infrastructure automatically optimizes playback quality based on network conditions and device capabilities.

How It Works

HesedVid’s playback system provides:

  • Global CDN: Videos cached at edge locations worldwide for low latency
  • Adaptive Bitrate: Automatic quality switching based on bandwidth
  • Multiple Formats: HLS and CMAF for broad compatibility
  • Security: Signed URLs for private content access control
  • Analytics: Real-time playback metrics and quality tracking

Public Video Playback

For videos with allowPublicAccess: true, stream directly from our CDN without authentication.

Public URL Format

https://worker.hesedvid.com/v1/public/{org_id}/{env_id}/{vid_id}/master.m3u8

Inline public metadata has been removed. Use the path-based scheme above.

Example URLs

ComponentExample
Public Path/v1/public/org_123/env_456/vid_789/master.m3u8
720p Playlist/v1/public/org_123/env_456/vid_789/video-720p.m3u8
Init/Segment/v1/public/org_123/env_456/vid_789/video/init.m4s

Private Video Playback

For videos with allowPublicAccess: false, request a signed URL with authentication token.

Get Signed Playback URL

/{orgID}/videos/{publicID}/signed-url

Generate a time-limited, authenticated URL for private video access.

Authentication

All requests require authentication via API key:

X-Api-Key: hv_live_123456789abcdef...

Path Parameters

ParameterTypeRequiredDescription
orgIDstringYesYour organization ID (e.g., org_123456789)
publicIDstringYesVideo public ID (e.g., vid_AbCdEfGhIjKlMnOp)

Query Parameters

ParameterTypeRequiredDefaultDescription
expirationstringNo1hToken expiration time
domainsstringNo-Comma-separated allowed domains

Expiration Formats

FormatDurationUse Case
15m15 minutesShort-term access
30m30 minutesTemporary viewing
1h1 hourDefault, general use
4h4 hoursExtended sessions
24h24 hoursLong-term access
7d7 daysMaximum duration

Response

Success Response (200 OK)
{
"body": {
"signed_url": "https://worker.hesedvid.com/v1/private/vid_AbCdEfGhIjKlMnOp/master.m3u8?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
}
FieldTypeDescription
signed_urlstringComplete URL with authentication token
expires_inintegerSeconds until token expiration
Error Responses
StatusErrorDescription
400invalid_expirationExpiration time exceeds maximum (7 days)
401unauthorizedInvalid or missing API key
403forbiddenInsufficient permissions for video
404video_not_foundVideo doesn’t exist or is deleted
429rate_limit_exceededToo many signed URL requests

Example: Request Signed URL

Caution

Signed URLs contain authentication tokens. Never expose them in client-side source code, share them publicly, or log them in plain text. Store them securely and refresh them before expiration.

Video Player Integration

HLS.js Integration

HLS.js is the most popular JavaScript library for HLS playback with broad browser support.

Basic Setup

<!DOCTYPE html>
<html>
<head>
<title>HesedVid Player</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video" controls width="100%" height="400"></video>
<script>
const video = document.getElementById('video');
const videoSrc = 'https://worker.hesedvid.com/v1/private/vid_AbCdEfGhIjKlMnOp/master.m3u8?token=...';
if (Hls.isSupported()) {
const hls = new Hls({
enableWorker: true,
lowLatencyMode: false,
backBufferLength: 90
});
hls.loadSource(videoSrc);
hls.attachMedia(video);
// Error handling
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
console.error('Fatal error:', data.type, data.details);
switch(data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
console.log('Network error, trying to recover...');
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log('Media error, trying to recover...');
hls.recoverMediaError();
break;
default:
console.log('Fatal error, cannot recover');
hls.destroy();
break;
}
}
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Native HLS support (Safari)
video.src = videoSrc;
} else {
console.error('HLS is not supported in this browser');
}
</script>
</body>
</html>

Advanced Configuration

const hls = new Hls({
// Performance settings
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90,
maxBufferLength: 30,
// Quality settings
startLevel: -1, // Auto-detect starting quality
capLevelToPlayerSize: true,
// Network settings
maxLoadingDelay: 4,
maxBufferHole: 0.5,
// Debugging
debug: false,
enableWorker: true
});
// Quality change events
hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
const level = hls.levels[data.level];
console.log(`Switched to ${level.height}p (${level.bitrate} kbps)`);
});
// Buffer events
hls.on(Hls.Events.BUFFER_STALLED, () => {
console.log('Buffer stalled - network issue');
});
hls.on(Hls.Events.BUFFER_RESUMED, () => {
console.log('Buffer resumed');
});

Video.js Integration

Video.js provides a more feature-rich player with built-in HLS support.

<!DOCTYPE html>
<html>
<head>
<title>HesedVid Video.js Player</title>
<link href="https://vjs.zencdn.net/8.6.1/video-js.css" rel="stylesheet">
</head>
<body>
<video-js
id="my-video"
class="vjs-default-skin vjs-big-play-centered"
controls
preload="auto"
width="100%"
height="400"
data-setup='{}'>
</video-js>
<script src="https://vjs.zencdn.net/8.6.1/video.min.js"></script>
<script>
const player = videojs('my-video');
player.src({
src: 'https://worker.hesedvid.com/v1/private/vid_AbCdEfGhIjKlMnOp/master.m3u8?token=...',
type: 'application/x-mpegURL'
});
// Quality change events
player.on('loadedmetadata', () => {
console.log('Video metadata loaded');
});
player.on('error', (error) => {
console.error('Player error:', error);
});
</script>
</body>
</html>

React Integration

import React, { useEffect, useRef } from 'react';
import Hls from 'hls.js';
const HesedVidPlayer = ({ videoSrc, onError }) => {
const videoRef = useRef(null);
const hlsRef = useRef(null);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
if (Hls.isSupported()) {
const hls = new Hls({
enableWorker: true,
lowLatencyMode: false
});
hls.loadSource(videoSrc);
hls.attachMedia(video);
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal && onError) {
onError(data);
}
});
hlsRef.current = hls;
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
return () => {
if (hlsRef.current) {
hlsRef.current.destroy();
}
};
}, [videoSrc, onError]);
return (
<video
ref={videoRef}
controls
width="100%"
height="400"
/>
);
};
export default HesedVidPlayer;

Adaptive Bitrate Streaming

HesedVid automatically generates multiple quality levels based on your video quality settings:

Quality Presets

PresetResolutionsBitratesUse Case
standard360p, 480p, 720p500-2500 kbpsGeneral content, mobile
high480p, 720p, 1080p1000-5000 kbpsPremium content, desktop
ultra720p, 1080p, 1440p, 2160p2000-15000 kbpsProfessional content, 4K

Quality Switching

The player automatically switches between qualities based on:

  • Network bandwidth: Higher quality for faster connections
  • Device capabilities: Appropriate resolution for screen size
  • Buffer health: Lower quality if buffering issues occur
  • CPU usage: Reduced quality on slower devices

Manual Quality Control

// Get available quality levels
const levels = hls.levels;
console.log('Available qualities:', levels.map(level => ({
height: level.height,
bitrate: level.bitrate,
width: level.width
})));
// Set specific quality level
hls.currentLevel = 2; // Switch to level 2
// Enable manual quality selection
hls.on(Hls.Events.MANIFEST_PARSED, () => {
// Add quality selector to UI
const qualitySelect = document.getElementById('quality-select');
levels.forEach((level, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${level.height}p (${Math.round(level.bitrate/1000)}kbps)`;
qualitySelect.appendChild(option);
});
qualitySelect.addEventListener('change', (e) => {
hls.currentLevel = parseInt(e.target.value);
});
});

Low Latency Streaming

Enable low-latency mode for near real-time playback (2-6 second latency):

Configuration

const hls = new Hls({
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90,
maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, // 60MB
maxBufferHole: 0.1
});

Low Latency Features

  • LL-HLS: Low Latency HLS with CMAF chunks
  • Bootstrap segments: Faster startup time
  • Reduced buffering: Minimal pre-roll delay
  • Real-time updates: Live-like experience for on-demand content

Tip

Low latency mode increases bandwidth usage and may cause more quality switches. Use only when real-time playback is essential.

CORS and Security

CORS Configuration

Our CDN includes permissive CORS headers for web applications:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Range, Content-Type
Access-Control-Max-Age: 3600

Domain Restrictions

For signed URLs, you can restrict playback to specific domains:

const response = await fetch(
`https://api.hesedvid.com/v1/api/org_123/videos/${videoID}/signed-url?domains=example.com,app.example.com`,
{
headers: {
'X-Api-Key': 'hv_live_123456789abcdef...'
}
}
);

Token Security

  • JWT Tokens: Signed URLs use JWT tokens for authentication
  • Expiration: Tokens automatically expire for security
  • Domain Binding: Tokens can be bound to specific domains
  • Single Use: Tokens are validated on each request

Direct Segment Access

Access individual segments or renditions directly for advanced use cases:

URL Patterns

ComponentPatternExample
Master Playlist/{publicID}/master.m3u8/pubvid_AbCdEf/master.m3u8
Rendition Playlist/{publicID}/{quality}.m3u8/pubvid_AbCdEf/720p.m3u8
Video Segments/{publicID}/{quality}/{segment}.ts/pubvid_AbCdEf/720p/segment-0001.ts
Init Segments/{publicID}/{quality}/init.mp4/pubvid_AbCdEf/720p/init.mp4

Advanced Playlist Access

// Fetch master playlist
const masterResponse = await fetch('https://worker.hesedvid.com/pubvid_AbCdEf/master.m3u8');
const masterPlaylist = await masterResponse.text();
// Parse available renditions
const renditions = masterPlaylist.match(/#EXT-X-STREAM-INF:.*\n([^\n]+)/g);
renditions.forEach(rendition => {
const resolution = rendition.match(/RESOLUTION=(\d+x\d+)/);
const bandwidth = rendition.match(/BANDWIDTH=(\d+)/);
console.log(`Resolution: ${resolution[1]}, Bandwidth: ${bandwidth[1]} bps`);
});

Query Parameters

Additional parameters for playback optimization:

ParameterTypeDescription
tokenstringAuthentication token (for private videos)
bootstrapbooleanInclude low-latency bootstrap segments
_HLS_msnintegerMedia sequence number (for low latency)
_HLS_partintegerPart number (for low latency)
_HLS_skipbooleanSkip segments for faster startup

Performance Monitoring

Player Events

Monitor playback quality and performance:

// Quality change tracking
hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => {
const level = hls.levels[data.level];
console.log(`Quality changed to ${level.height}p (${level.bitrate} kbps)`);
// Send analytics
analytics.track('quality_change', {
resolution: level.height,
bitrate: level.bitrate,
reason: data.reason
});
});
// Buffer health monitoring
hls.on(Hls.Events.BUFFER_STALLED, () => {
console.log('Buffer stalled - network issue');
analytics.track('buffer_stall');
});
hls.on(Hls.Events.BUFFER_RESUMED, () => {
console.log('Buffer resumed');
analytics.track('buffer_resume');
});
// Error tracking
hls.on(Hls.Events.ERROR, (event, data) => {
if (data.fatal) {
console.error('Fatal error:', data.type, data.details);
analytics.track('fatal_error', {
type: data.type,
details: data.details
});
}
});

Analytics Integration

// Track playback metrics
const trackPlaybackMetrics = () => {
const video = document.getElementById('video');
video.addEventListener('loadstart', () => {
analytics.track('playback_started');
});
video.addEventListener('canplay', () => {
analytics.track('playback_ready');
});
video.addEventListener('ended', () => {
analytics.track('playback_completed');
});
video.addEventListener('timeupdate', () => {
const progress = (video.currentTime / video.duration) * 100;
if (progress % 25 === 0) { // Track 25%, 50%, 75%, 100%
analytics.track('playback_progress', { progress });
}
});
};

Best Practices

Performance Optimization

  • Use appropriate preload settings: Set preload="metadata" to improve page load without downloading video content.

  • Implement lazy loading: Only load video players when they’re about to be viewed.

  • Monitor bandwidth usage: Track quality switches and buffer events to optimize user experience.

  • Cache signed URLs: Store signed URLs temporarily to reduce API calls, but refresh before expiration.

  • Error Handling

    const handlePlaybackError = (error) => {
    switch(error.type) {
    case Hls.ErrorTypes.NETWORK_ERROR:
    console.log('Network error - retrying...');
    setTimeout(() => hls.startLoad(), 1000);
    break;
    case Hls.ErrorTypes.MEDIA_ERROR:
    console.log('Media error - recovering...');
    hls.recoverMediaError();
    break;
    case Hls.ErrorTypes.MUX_ERROR:
    console.log('Mux error - cannot recover');
    showErrorMessage('Video playback failed. Please refresh the page.');
    break;
    default:
    console.log('Unknown error:', error);
    break;
    }
    };

    Security Considerations

    • Never expose signed URLs in client-side source code
    • Implement domain restrictions for sensitive content
    • Use appropriate expiration times based on your use case
    • Monitor token usage for security breaches
    • Implement rate limiting on your signed URL requests

    Troubleshooting

    Common Issues

    IssueCauseSolution
    Video won’t playInvalid URL or expired tokenCheck URL format and token expiration
    Poor qualityNetwork bandwidth issuesImplement quality selection UI
    Buffering issuesSlow network or server issuesCheck network connection and CDN status
    CORS errorsDomain not allowedCheck CORS configuration and domain restrictions
    Token errorsInvalid or expired tokenRequest new signed URL

    Debug Mode

    Enable debug mode for detailed logging:

    const hls = new Hls({
    debug: true,
    enableWorker: true
    });
    // Additional debugging
    hls.on(Hls.Events.MANIFEST_PARSED, () => {
    console.log('Manifest parsed:', hls.levels);
    });
    hls.on(Hls.Events.FRAG_LOADED, (event, data) => {
    console.log('Fragment loaded:', data.frag.url);
    });

    Support

    For additional help:

    • Check our health endpoint GET /health for service issues
    • Review error codes for detailed explanations
    • Contact support with your organization ID and video ID for assistance

    Public playback

    Use the path-based public URL:

    https://worker.hesedvid.com/v1/public/{org_id}/{env_id}/{vid_id}/master.m3u8

    Note: For private videos, the worker rewrites playlists on the fly so the token propagates to all variant and segment requests automatically. You only need to use the returned signed_url for the initial load.