HLS Plugin¶
Description¶
The HLS (HTTP Live Streaming) plugin provides HTTP Live Streaming capabilities for CVEDIA-RT, enabling real-time video streaming over HTTP networks. It implements the HLS protocol with adaptive bitrate streaming, automatic segmentation, and M3U8 playlist generation for live video distribution to web browsers, mobile devices, and media players.
Key Features¶
- HTTP Live Streaming: Standards-compliant HLS implementation
- Automatic Segmentation: Real-time video segmentation into .ts files
- M3U8 Playlist Generation: Dynamic playlist creation and management
- Web Server Integration: Built-in HTTP server for stream delivery
- Configurable Segment Duration: Adjustable segment length for latency optimization
- Memory-based Streaming: In-memory segment storage for low-latency delivery
- Multi-stream Support: Handle multiple concurrent HLS streams
When to Use¶
- Live video streaming to web browsers
- Mobile device video streaming
- Real-time surveillance video distribution
- Video analytics result streaming with overlays
- Multi-client video broadcasting
- Low-latency video delivery over HTTP
Requirements¶
Software Dependencies¶
- FFmpeg libraries (libavformat, libavcodec)
- OpenCV for frame processing
- HTTP server capability (built-in webserver)
- Network connectivity for client access
Hardware Requirements¶
- Sufficient CPU for H.264 encoding
- Adequate memory for segment buffering
- Network bandwidth for concurrent clients
- Storage space for temporary segments (if file-based)
Configuration¶
Basic Configuration¶
{
"output": {
"handlers": {
"hls-stream": {
"uri": "hls://live-stream",
"sink": "output",
"enabled": true,
"segment_duration": 2
}
}
}
}
Advanced Configuration¶
{
"output": {
"handlers": {
"primary-hls": {
"uri": "hls://main-stream",
"sink": "video",
"enabled": true,
"segment_duration": 2,
"max_segments": 10,
"codec": "h264",
"bitrate": 2000000,
"fps": 25,
"resolution": {
"width": 1920,
"height": 1080
}
},
"mobile-hls": {
"uri": "hls://mobile-stream",
"sink": "video",
"enabled": true,
"segment_duration": 4,
"max_segments": 5,
"codec": "h264",
"bitrate": 800000,
"fps": 15,
"resolution": {
"width": 1280,
"height": 720
}
}
},
"webserver": {
"port": 8080,
"cors_enabled": true,
"cors_origins": ["*"],
"max_connections": 100
}
}
}
Configuration Schema¶
Parameter | Type | Default | Description |
---|---|---|---|
uri |
string | required | HLS stream identifier (hls://stream-name) |
sink |
string | "output" | Data sink to connect to |
enabled |
boolean | true | Enable/disable stream |
segment_duration |
integer | 2 | Segment duration in seconds |
max_segments |
integer | 10 | Maximum segments in playlist |
codec |
string | "h264" | Video codec (h264, hevc) |
bitrate |
integer | 2000000 | Target bitrate in bits/sec |
fps |
integer | 25 | Target frames per second |
resolution.width |
integer | auto | Video width in pixels |
resolution.height |
integer | auto | Video height in pixels |
Stream Access¶
Stream URLs¶
HLS streams are automatically served through the built-in web server:
- M3U8 Playlist:
http://localhost:8080/hls/{stream-name}.m3u8
- Video Segments:
http://localhost:8080/hls/{stream-name}/{segment-id}.ts
Where {stream-name}
corresponds to the instance ID or configured stream name.
Client Access¶
<!-- HTML5 Video Player -->
<video controls>
<source src="http://localhost:8080/hls/stream-name.m3u8" type="application/vnd.apple.mpegurl">
Your browser does not support HLS.
</source>
<!-- With HLS.js for broader browser support -->
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<video id="video" controls></video>
<script>
const video = document.getElementById('video');
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource('http://localhost:8080/hls/stream-name.m3u8');
hls.attachMedia(video);
}
</script>
API Reference¶
C++ API¶
The HLS plugin implements the iface::OutputHandler
interface:
class HLSOutput : public iface::OutputHandler {
public:
// Constructor
HLSOutput(const std::string& moduleName,
const std::string& schema,
const std::string& sink,
const std::string& path,
pCValue config);
// Factory method
static std::shared_ptr<iface::OutputHandler> create(
const std::string& moduleName,
const std::string& schema,
const std::string& sink,
const std::string& path,
pCValue config);
// Stream control
expected<bool> write(pCValue sinkData = VAL(),
std::string dynamicPath = "") override;
void stop() override;
void close() override;
// Stream information
std::string getSink() override;
};
Core HLS Classes¶
class HlsCore {
public:
struct config {
int segmentDuration = 2; // Segment duration in seconds
};
// Stream management
expected<bool> write(cbuffer frame, const std::string& feedName);
void close(const std::string& feedName);
};
class HlsStream {
public:
// Stream control
HlsStream(const std::string& name, int segmentDuration = 2);
void updateFrame(cbuffer frame);
// Stream information
std::string getName() const;
uint64_t getSegmentIndex() const;
std::string getM3u8() const;
// Segment access
expected<std::shared_ptr<Segment>> getVideoSegment(const std::string& key) const;
};
Lua API¶
The HLS plugin is integrated through the Output plugin system and doesn't expose direct Lua bindings. It's configured and used through the Output plugin:
-- HLS streams are created through the Output plugin
local output = api.factory.output.create(instance, "HLSStreamer")
-- Add HLS handler through Output plugin
local hlsHandler = output:addHandler(
"hls-stream",
"hls://live-feed",
"video",
{
segment_duration = 2,
max_segments = 10
}
)
Note: HLS plugin operates as an output handler registered with the URI scheme "hls://".
Examples¶
Basic Live Streaming¶
-- Create output instance
local instance = api.thread.getCurrentInstance()
local output = api.factory.output.create(instance, "LiveStreamer")
-- Configure HLS streaming
local hlsConfig = {
segment_duration = 2,
max_segments = 8,
fps = 25
}
-- Add HLS handler
local stream = output:addHandler(
"live-stream",
"hls://main-feed",
"output",
hlsConfig
)
if stream then
print("HLS stream available at: http://localhost:8080/hls/main-feed.m3u8")
else
print("Failed to create HLS stream")
end
Multi-Quality Streaming¶
local output = api.factory.output.create(instance, "AdaptiveStreamer")
-- High quality stream
local hdConfig = {
segment_duration = 2,
bitrate = 3000000,
resolution = { width = 1920, height = 1080 },
fps = 30
}
local hdStream = output:addHandler(
"hd-stream",
"hls://hd-feed",
"output",
hdConfig
)
-- Mobile quality stream
local mobileConfig = {
segment_duration = 4,
bitrate = 800000,
resolution = { width = 1280, height = 720 },
fps = 15
}
local mobileStream = output:addHandler(
"mobile-stream",
"hls://mobile-feed",
"output",
mobileConfig
)
-- Print stream URLs
if hdStream then
print("HD Stream: http://localhost:8080/hls/hd-feed.m3u8")
end
if mobileStream then
print("Mobile Stream: http://localhost:8080/hls/mobile-feed.m3u8")
end
Analytics Overlay Streaming¶
-- Create output for analytics results with overlays
local output = api.factory.output.create(instance, "AnalyticsStreamer")
-- Configure for analytics streaming
local analyticsConfig = {
segment_duration = 1, -- Low latency for real-time analytics
max_segments = 5,
fps = 15,
include_overlays = true
}
local analyticsStream = output:addHandler(
"analytics-stream",
"hls://analytics-feed",
"rendered", -- Use rendered sink with overlays
analyticsConfig
)
-- Function to toggle overlay visibility
function toggleOverlays(enabled)
local config = output:getConfig()
config.handlers["analytics-stream"].include_overlays = enabled
output:saveConfig(config)
end
print("Analytics stream: http://localhost:8080/hls/analytics-feed.m3u8")
Stream Health Monitoring¶
local output = api.factory.output.create(instance, "MonitoredStreamer")
-- Create stream with monitoring
local streamConfig = {
segment_duration = 2,
max_segments = 10,
health_check_interval = 30 -- seconds
}
local stream = output:addHandler(
"monitored-stream",
"hls://health-monitored",
"output",
streamConfig
)
-- Monitor stream health
function checkStreamHealth()
-- Check if stream is still active
local config = output:getConfig()
local streamName = "monitored-stream"
if config.handlers[streamName] then
print("Stream is active: " .. streamName)
return true
else
print("Stream is inactive: " .. streamName)
return false
end
end
-- Health check should be implemented in your main processing loop
-- Example: Check every N frames or use a frame counter
-- No built-in setInterval exists in the Lua API
Dynamic Stream Management¶
local output = api.factory.output.create(instance, "DynamicStreamer")
-- Function to create stream dynamically
function createStream(streamName, quality)
local configs = {
high = {
segment_duration = 2,
bitrate = 4000000,
resolution = { width = 1920, height = 1080 },
fps = 30
},
medium = {
segment_duration = 3,
bitrate = 2000000,
resolution = { width = 1280, height = 720 },
fps = 25
},
low = {
segment_duration = 4,
bitrate = 800000,
resolution = { width = 854, height = 480 },
fps = 15
}
}
local config = configs[quality] or configs.medium
local stream = output:addHandler(
streamName,
"hls://" .. streamName,
"output",
config
)
if stream then
local url = string.format("http://localhost:8080/hls/%s.m3u8", streamName)
print(string.format("Created %s quality stream: %s", quality, url))
return url
end
return nil
end
-- Function to remove stream
function removeStream(streamName)
local success = output:removeHandler(streamName)
if success then
print("Removed stream: " .. streamName)
else
print("Failed to remove stream: " .. streamName)
end
return success
end
-- Dynamic stream creation
local hdUrl = createStream("camera-01-hd", "high")
local mobileUrl = createStream("camera-01-mobile", "low")
-- Later, remove streams
-- removeStream("camera-01-hd")
Best Practices¶
Stream Configuration¶
-
Segment Duration:
- Use 2-6 seconds for good latency/quality balance
- Shorter segments = lower latency, higher overhead
- Longer segments = higher latency, better compression
-
Playlist Length:
- Keep 3-10 segments in playlist
- More segments = better seeking, higher memory usage
- Fewer segments = lower memory, limited seeking
-
Bitrate Selection:
- Match bitrate to content complexity and network capacity
- Use adaptive streaming for varied client capabilities
- Monitor bandwidth usage vs. quality trade-offs
Performance Optimization¶
-
Memory Management:
- Monitor segment buffer usage
- Implement proper cleanup for stopped streams
- Use appropriate max_segments limits
-
CPU Usage:
- Balance encoding quality vs. CPU load
- Consider hardware acceleration when available
- Monitor frame processing rates
-
Network Optimization:
- Use CDN for large-scale distribution
- Implement client-side buffering
- Monitor concurrent connection limits
Security Considerations¶
-
Access Control:
- Implement authentication for sensitive streams
- Use HTTPS for secure transmission
- Restrict access by IP or user credentials
-
Content Protection:
- Consider HLS encryption for sensitive content
- Implement token-based access control
- Monitor for unauthorized access attempts
Troubleshooting¶
Stream Creation Issues¶
-
"Failed to create HLS stream"
- Check if webserver port is available
- Verify FFmpeg libraries are installed
- Ensure sufficient system resources
- Check for conflicting stream names
-
"Segment generation failed"
- Verify video codec support
- Check input frame format compatibility
- Monitor CPU usage during encoding
- Validate segment duration settings
Playback Issues¶
-
"Stream not loading in browser"
- Verify M3U8 URL accessibility
- Check CORS settings for cross-origin requests
- Ensure HLS.js is loaded for unsupported browsers
- Validate playlist format
-
"Stuttering or buffering"
- Check network bandwidth vs. bitrate
- Reduce segment duration for lower latency
- Monitor server CPU usage
- Verify client-side buffer settings
-
"Old content playing"
- Check segment cleanup process
- Verify playlist updates
- Clear browser cache
- Check segment naming conflicts
Performance Issues¶
-
"High CPU usage"
- Reduce encoding quality settings
- Lower frame rate or resolution
- Check for multiple concurrent encodings
- Consider hardware acceleration
-
"Memory usage growing"
- Verify segment cleanup on stream stop
- Check max_segments configuration
- Monitor for memory leaks
- Implement periodic garbage collection
-
"Network congestion"
- Reduce bitrate or resolution
- Implement client-side adaptive streaming
- Use content delivery network (CDN)
- Monitor concurrent client connections
Integration Examples¶
Web Player Integration¶
<!DOCTYPE html>
<html>
<head>
<title>CVEDIA-RT HLS Stream</title>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>
<body>
<video id="video" controls width="800" height="600"></video>
<script>
const video = document.getElementById('video');
const streamUrl = 'http://localhost:8080/hls/main-feed.m3u8';
if (Hls.isSupported()) {
const hls = new Hls({
debug: false,
enableWorker: true,
lowLatencyMode: true,
backBufferLength: 90
});
hls.loadSource(streamUrl);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
console.log('Stream loaded successfully');
video.play();
});
hls.on(Hls.Events.ERROR, function(event, data) {
console.error('HLS Error:', data);
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// Native HLS support (Safari)
video.src = streamUrl;
} else {
console.error('HLS not supported');
}
</script>
</body>
</html>
Mobile App Integration¶
// React Native example
import Video from 'react-native-video';
const HLSPlayer = () => {
return (
<Video
source={{ uri: 'http://192.168.1.100:8080/hls/mobile-feed.m3u8' }}
style={{ width: '100%', height: 200 }}
controls={true}
resizeMode="contain"
onError={(error) => console.log('Video Error:', error)}
onLoad={() => console.log('Video loaded')}
/>
);
};
RTMP to HLS Bridge¶
-- Bridge RTMP input to HLS output
local input = api.factory.gstreamerreader.create(instance, "RTMPInput")
local output = api.factory.output.create(instance, "HLSOutput")
-- Configure RTMP input
input:setConfig({
uri = "rtmp://source.example.com/live/stream",
format = "h264"
})
-- Configure HLS output
local hlsStream = output:addHandler(
"rtmp-bridge",
"hls://rtmp-bridge",
"video",
{
segment_duration = 3,
bitrate = 2000000
}
)
-- Start processing
input:start()
print("RTMP to HLS bridge: http://localhost:8080/hls/rtmp-bridge.m3u8")
See Also¶
- Output Plugins Overview
- GStreamerWriter Plugin - Advanced video output
- Output Plugin - Main output system
- Input Plugins - Video input sources
- Webserver Configuration
- FFmpeg Integration Guide