HLS Extension
HTTP Live Streaming extension providing production-ready video streaming capabilities with adaptive bitrate streaming, transcoding, and distributed support
The HLS (HTTP Live Streaming) extension provides production-ready video streaming capabilities with adaptive bitrate streaming, transcoding, and distributed support for the Forge framework.
Features
Core Streaming Features
- Adaptive Bitrate Streaming: Automatically switches between quality levels based on network conditions
- Live Streaming: Real-time live streams with DVR capabilities and configurable window size
- Video on Demand (VOD): Convert video files to HLS format with multiple quality levels
- Multiple Stream Types: Support for live, VOD, and event streaming
Media Processing
- FFmpeg-based Transcoding: Multi-resolution and multi-bitrate transcoding
- Automatic Segmentation: Video segmentation into HLS-compatible chunks
- Hardware Acceleration: Support for CUDA, QuickSync, VideoToolbox, and other hardware encoders
- Configurable Profiles: Predefined quality profiles (360p, 480p, 720p, 1080p, 4K)
- Custom Codecs: Support for H.264, H.265, VP9 video and AAC, Opus audio codecs
Storage & Distribution
- Multiple Storage Backends: Integration with Forge storage extension (Local, S3, GCS, Azure)
- Distributed Support: Multi-node deployment with Raft consensus for high availability
- Auto Cleanup: Automatic cleanup of old segments with configurable retention
- CDN Ready: Optimized for CDN distribution with proper caching headers
Advanced Features
- CORS Support: Built-in CORS headers for cross-origin playback
- Health Monitoring: Integrated health checks and performance metrics
- Statistics Tracking: Viewer counts, bandwidth usage, and performance analytics
- Leadership Management: Automatic failover and load balancing in distributed mode
Installation
go get github.com/xraph/forge/extensions/hlsPrerequisites
The HLS extension requires FFmpeg and FFprobe to be installed on your system:
# Ubuntu/Debian
sudo apt-get install ffmpeg
# macOS
brew install ffmpeg
# Verify installation
ffmpeg -version
ffprobe -versionImportant: The Forge storage extension must be registered before the HLS extension.
Quick Start
Basic HLS Server
package main
import (
"context"
"log"
"github.com/xraph/forge"
"github.com/xraph/forge/extensions/hls"
"github.com/xraph/forge/extensions/storage"
)
func main() {
app := forge.New()
// Register storage extension (required)
app.UseExtension(storage.NewExtension(storage.Config{
Backends: map[string]storage.BackendConfig{
"default": {
Type: "local",
Config: map[string]interface{}{
"base_path": "./hls_storage",
},
},
},
Default: "default",
}))
// Register HLS extension
app.UseExtension(hls.NewExtension(
hls.WithBasePath("/hls"),
hls.WithBaseURL("http://localhost:8080/hls"),
hls.WithTargetDuration(6),
hls.WithTranscoding(true, hls.DefaultProfiles()...),
))
ctx := context.Background()
if err := app.Start(ctx); err != nil {
log.Fatal(err)
}
log.Println("HLS server started on http://localhost:8080")
app.Listen(":8080")
}Creating a Live Stream
// Get HLS service from DI container
hlsSvc, err := forge.Resolve[hls.HLS](app.Container(), "hls")
if err != nil {
log.Fatal(err)
}
// Create a live stream
stream, err := hlsSvc.CreateStream(ctx, hls.StreamOptions{
Type: hls.StreamTypeLive,
Title: "Live Event",
Description: "Live streaming event",
TargetDuration: 6,
DVRWindowSize: 10,
TranscodeProfiles: []hls.TranscodeProfile{
hls.Profile720p,
hls.Profile1080p,
},
})
if err != nil {
log.Fatal(err)
}
// Start the live stream
err = hlsSvc.StartLiveStream(ctx, stream.ID)
if err != nil {
log.Fatal(err)
}
log.Printf("Live stream available at: %s/%s/master.m3u8", baseURL, stream.ID)Creating VOD Content
// Create VOD from file
stream, err := hlsSvc.CreateVOD(ctx, fileReader, hls.VODOptions{
Title: "Movie Title",
Description: "Movie description",
TargetDuration: 10,
TranscodeProfiles: []hls.TranscodeProfile{
hls.Profile360p,
hls.Profile480p,
hls.Profile720p,
hls.Profile1080p,
},
})
if err != nil {
log.Fatal(err)
}
log.Printf("VOD available at: %s/%s/master.m3u8", baseURL, stream.ID)Configuration
Programmatic Configuration
hlsExt := hls.NewExtension(
// Server settings
hls.WithBasePath("/hls"),
hls.WithBaseURL("http://localhost:8080/hls"),
// Storage settings
hls.WithStorageBackend("s3"),
hls.WithStoragePrefix("hls"),
// Stream settings
hls.WithTargetDuration(6), // 6 second segments
hls.WithDVRWindow(10), // Keep last 10 segments
hls.WithMaxSegmentSize(50*1024*1024), // 50MB max segment size
// Transcoding settings
hls.WithTranscoding(true,
hls.Profile360p,
hls.Profile720p,
hls.Profile1080p,
),
hls.WithFFmpegPaths("/usr/bin/ffmpeg", "/usr/bin/ffprobe"),
hls.WithHardwareAccel(true, "cuda"),
hls.WithMaxTranscodeJobs(4),
// CORS and cleanup
hls.WithCORS(true, "*"),
hls.WithCleanup(24*time.Hour, 1*time.Hour),
)YAML Configuration
hls:
enabled: true
base_path: "/hls"
base_url: "https://cdn.example.com/hls"
# Storage configuration
storage_backend: "s3"
storage_prefix: "hls"
# Stream configuration
target_duration: 6
dvr_window_size: 10
max_segment_size: 52428800 # 50MB
# Transcoding configuration
enable_transcoding: true
ffmpeg_path: "/usr/bin/ffmpeg"
ffprobe_path: "/usr/bin/ffprobe"
enable_hardware_accel: true
hardware_accel_type: "cuda"
max_transcode_jobs: 4
transcode_profiles:
- name: "360p"
width: 640
height: 360
bitrate: 800000
frame_rate: 30.0
video_codec: "h264"
audio_codec: "aac"
preset: "fast"
crf: 23
- name: "720p"
width: 1280
height: 720
bitrate: 2500000
frame_rate: 30.0
video_codec: "h264"
audio_codec: "aac"
preset: "fast"
crf: 23
# CORS configuration
enable_cors: true
cors_origins: ["*"]
# Cleanup configuration
cleanup_interval: "1h"
segment_retention: "24h"Environment Variables
# Server settings
HLS_ENABLED=true
HLS_BASE_PATH="/hls"
HLS_BASE_URL="https://cdn.example.com/hls"
# Storage settings
HLS_STORAGE_BACKEND="s3"
HLS_STORAGE_PREFIX="hls"
# Stream settings
HLS_TARGET_DURATION=6
HLS_DVR_WINDOW_SIZE=10
HLS_MAX_SEGMENT_SIZE=52428800
# Transcoding settings
HLS_ENABLE_TRANSCODING=true
HLS_FFMPEG_PATH="/usr/bin/ffmpeg"
HLS_FFPROBE_PATH="/usr/bin/ffprobe"
HLS_ENABLE_HARDWARE_ACCEL=true
HLS_HARDWARE_ACCEL_TYPE="cuda"
HLS_MAX_TRANSCODE_JOBS=4
# CORS settings
HLS_ENABLE_CORS=true
HLS_CORS_ORIGINS="*"
# Cleanup settings
HLS_CLEANUP_INTERVAL="1h"
HLS_SEGMENT_RETENTION="24h"Distributed Mode
The HLS extension supports distributed deployment for high availability and horizontal scaling using the Forge consensus extension.
Requirements
- Consensus Extension: Raft-based leader election and coordination
- Shared Storage: S3, GCS, Azure, or shared filesystem
- Load Balancer: Optional for distributing requests
Configuration
// Register consensus extension first
consensusExt := consensus.NewExtension(
consensus.WithNodeID("hls-node-1"),
consensus.WithClusterID("hls-cluster"),
consensus.WithBindAddress("0.0.0.0:7000"),
consensus.WithTransportType("tcp"),
)
// Configure HLS with distributed mode
hlsExt := hls.NewExtension(
hls.WithDistributed(true),
hls.WithNodeID("hls-node-1"),
hls.WithClusterID("hls-cluster"),
hls.WithFailover(true),
hls.WithStorageBackend("s3"), // Shared storage required
)
app.UseExtension(storageExt) // Storage extension
app.UseExtension(consensusExt) // Consensus extension (required)
app.UseExtension(hlsExt) // HLS extensionDistributed Features
- Leader Election: Automatic leader election using Raft consensus
- Write Operations: Stream creation/deletion only on leader node
- Read Operations: Playlists and segments available on all nodes
- Automatic Failover: Re-election on leader failure
- Cluster Awareness: Response headers showing node and leader info
- Request Routing: Automatic redirect to leader for write operations
Running a 3-Node Cluster
# Node 1 (Leader)
./app -node=hls-node-1 -port=8080 -raft=7000
# Node 2
./app -node=hls-node-2 -port=8081 -raft=7001 -peers=localhost:7000
# Node 3
./app -node=hls-node-3 -port=8082 -raft=7002 -peers=localhost:7000,localhost:7001API Endpoints
Stream Management
# Create a new stream
POST /hls/streams
Content-Type: application/json
{
"type": "live",
"title": "Live Event",
"description": "Live streaming event",
"target_duration": 6,
"dvr_window_size": 10,
"transcode_profiles": ["720p", "1080p"]
}
# List all streams
GET /hls/streams
# Get stream details
GET /hls/streams/{streamID}
# Delete a stream
DELETE /hls/streams/{streamID}Live Streaming
# Start live stream
POST /hls/streams/{streamID}/start
# Stop live stream
POST /hls/streams/{streamID}/stop
# Ingest segment
POST /hls/streams/{streamID}/ingest
Content-Type: video/mp2t
[Binary segment data]Playlist Endpoints
# Master playlist (adaptive bitrate)
GET /hls/{streamID}/master.m3u8
# Media playlist (specific quality)
GET /hls/{streamID}/variants/{variantID}/playlist.m3u8
# Video segment
GET /hls/{streamID}/variants/{variantID}/segment_{segmentNum}.tsStatistics
# Stream statistics
GET /hls/streams/{streamID}/stats
# Active viewers
GET /hls/streams/{streamID}/viewersTranscoding Profiles
Predefined Profiles
// Available predefined profiles
hls.Profile360p // 640x360, 800kbps
hls.Profile480p // 854x480, 1.2Mbps
hls.Profile720p // 1280x720, 2.5Mbps
hls.Profile1080p // 1920x1080, 5Mbps
hls.Profile4K // 3840x2160, 15Mbps
// Use default profiles
hls.DefaultProfiles() // Returns 360p, 720p, 1080pCustom Profiles
customProfile := hls.TranscodeProfile{
Name: "custom-720p",
Width: 1280,
Height: 720,
Bitrate: 3000000, // 3Mbps
FrameRate: 30.0,
VideoCodec: "h264",
AudioCodec: "aac",
Preset: "fast",
CRF: 23,
}
hlsExt := hls.NewExtension(
hls.WithTranscoding(true, customProfile),
)Hardware Acceleration
// NVIDIA CUDA
hls.WithHardwareAccel(true, "cuda")
// Intel QuickSync
hls.WithHardwareAccel(true, "qsv")
// Apple VideoToolbox (macOS)
hls.WithHardwareAccel(true, "videotoolbox")
// AMD AMF
hls.WithHardwareAccel(true, "amf")Client-Side Integration
HTML5 Video with HLS.js
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video" controls width="800"></video>
<script>
const video = document.getElementById('video');
const src = 'http://localhost:8080/hls/STREAM_ID/master.m3u8';
if (Hls.isSupported()) {
const hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90
});
hls.loadSource(src);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});
// Quality selection
hls.on(Hls.Events.MANIFEST_PARSED, function() {
const qualitySelect = document.getElementById('quality');
hls.levels.forEach((level, index) => {
const option = document.createElement('option');
option.value = index;
option.text = `${level.height}p (${Math.round(level.bitrate/1000)}k)`;
qualitySelect.appendChild(option);
});
});
function changeQuality(level) {
hls.currentLevel = level; // -1 for auto
}
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Native HLS support (Safari)
video.src = src;
}
</script>
</body>
</html>React Component
import React, { useEffect, useRef } from 'react';
import Hls from 'hls.js';
const HLSPlayer = ({ src, autoPlay = false }) => {
const videoRef = useRef(null);
const hlsRef = useRef(null);
useEffect(() => {
const video = videoRef.current;
if (Hls.isSupported()) {
const hls = new Hls({
enableWorker: true,
lowLatencyMode: true,
});
hls.loadSource(src);
hls.attachMedia(video);
if (autoPlay) {
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play();
});
}
hlsRef.current = hls;
return () => {
hls.destroy();
};
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = src;
}
}, [src, autoPlay]);
return (
<video
ref={videoRef}
controls
style={{ width: '100%', maxWidth: '800px' }}
/>
);
};
export default HLSPlayer;Monitoring and Analytics
Health Checks
The HLS extension automatically registers health checks:
GET /healthResponse includes HLS-specific health information:
{
"status": "healthy",
"extensions": {
"hls": {
"status": "healthy",
"active_streams": 5,
"total_viewers": 1250,
"storage_available": true,
"transcoder_jobs": 2
}
}
}Prometheus Metrics
# Active streams
hls_active_streams{type="live"} 3
hls_active_streams{type="vod"} 12
# Viewer metrics
hls_total_viewers 1250
hls_concurrent_viewers{stream_id="stream-123"} 45
# Bandwidth metrics
hls_bandwidth_bytes_total 1.5e+09
hls_bandwidth_bytes{stream_id="stream-123"} 2.5e+07
# Segment metrics
hls_segments_served_total 15420
hls_segments_created_total 8930
# Transcoding metrics
hls_transcode_jobs_active 2
hls_transcode_jobs_completed_total 156
hls_transcode_jobs_failed_total 3
hls_transcode_duration_seconds{profile="720p"} 45.2Custom Analytics
// Get stream statistics
stats, err := hlsSvc.GetStreamStats(ctx, streamID)
if err != nil {
log.Printf("Failed to get stats: %v", err)
return
}
log.Printf("Stream %s stats:", streamID)
log.Printf(" Viewers: %d", stats.CurrentViewers)
log.Printf(" Total views: %d", stats.TotalViews)
log.Printf(" Bandwidth: %d bytes/sec", stats.BandwidthBytesPerSec)
log.Printf(" Segments served: %d", stats.SegmentsServed)
log.Printf(" Uptime: %v", stats.Uptime)Best Practices
Performance Optimization
- Use Hardware Acceleration: Enable GPU encoding for better performance
- Optimize Segment Duration: 6-10 seconds for live, 10-15 seconds for VOD
- Limit Concurrent Jobs: Set appropriate limits for transcoding jobs
- Use CDN: Distribute content via CDN for global reach
- Monitor Resources: Track CPU, memory, and storage usage
Storage Considerations
- Use Shared Storage: Required for distributed deployments
- Enable Cleanup: Automatic cleanup prevents storage bloat
- Set Retention Policies: Balance storage costs with DVR requirements
- Use Compression: Enable storage compression when available
Security
- Authentication: Implement stream access control
- HTTPS: Use HTTPS for all HLS endpoints
- Token-based Access: Use signed URLs for segment access
- Rate Limiting: Implement rate limiting for API endpoints
Scalability
- Horizontal Scaling: Use distributed mode for high availability
- Load Balancing: Distribute requests across nodes
- Caching: Implement caching for playlists and segments
- Resource Planning: Plan for peak concurrent viewers
Troubleshooting
Common Issues
FFmpeg Not Found
Error: ffmpeg: executable file not found in $PATHSolution: Install FFmpeg or specify custom paths:
hls.WithFFmpegPaths("/usr/local/bin/ffmpeg", "/usr/local/bin/ffprobe")Storage Extension Not Found
Error: failed to resolve storage managerSolution: Register storage extension before HLS:
app.UseExtension(storageExt) // Must come first
app.UseExtension(hlsExt)Segments Not Playing
- Check FFmpeg logs for encoding errors
- Verify segments are saved to storage
- Ensure CORS headers are set for cross-origin requests
- Check browser console for HLS.js errors
High Memory Usage
- Reduce DVR window size
- Enable automatic cleanup
- Limit concurrent transcoding jobs
- Use streaming instead of buffering entire segments
Distributed Mode Issues
- Ensure consensus extension is registered first
- Verify shared storage is accessible from all nodes
- Check network connectivity between nodes
- Monitor leader election logs
Debug Mode
Enable debug logging for troubleshooting:
hlsExt := hls.NewExtension(
hls.WithDebug(true),
// ... other options
)Log Analysis
Monitor logs for common patterns:
# Transcoding errors
grep "transcode.*error" app.log
# Storage issues
grep "storage.*failed" app.log
# Consensus issues (distributed mode)
grep "consensus.*error" app.log
# High memory usage
grep "memory.*warning" app.logExamples
The HLS extension includes comprehensive examples:
- Basic Example: Simple live streaming setup
- VOD Example: Video on demand processing
- Distributed Example: 3-node cluster deployment
- Player Example: HTML5 player with HLS.js
See the examples directory for complete working examples.
Related Extensions
- Storage Extension: Required for HLS storage backends
- Consensus Extension: Required for distributed mode
- Dashboard Extension: Monitor HLS streams and performance
License
The HLS extension is part of the Forge framework and is licensed under the same terms.
How is this guide?
Last updated on