HLS for Dart
A robust, type-safe HLS (HTTP Live Streaming) library for Dart and Flutter. This library provides high-performance tools to Parse, Normalize, and Compose HLS playlists while maintaining strict compliance with RFC 8216.
🚀 Core Features
1. Universal Parsing
The library automatically detects whether a source is a Master or Media playlist. It handles URI resolution automatically if a baseUri is provided.
final manifest = await http.read(Uri.parse('https://example.com/video/master.m3u8'));
// Auto-detect and parse
final playlist = HlsParser.parse(manifest, baseUri: Uri.parse('https://example.com/video/'));
if (playlist is HlsMasterPlaylist) {
print('Found ${playlist.variants.length} variants');
}
2. Advanced Normalization
Normalization solves the headache of relative paths and variable placeholders. It transforms a playlist into its "final" form where all URIs are absolute and variables are injected.
// Resolves relative URIs to absolute and replaces {$VARIABLE} placeholders
final cleanPlaylist = HlsComposer.normalize(playlist);
print(cleanPlaylist.variants.first.uri);
// Output: https://example.com/video/1080p/index.m3u8
3. Exact Composition
Convert your HlsPlaylist objects back into valid .m3u8 strings. The composer ensures that types, quotes, and attributes follow the spec perfectly—ideal for playlist proxies or manipulators.
final String m3u8String = HlsComposer.compose(updatedPlaylist);
🛠Feature Deep-Dive
Media Playlist Management
Access granular data for VOD and Live streams, including initialization segments (#EXT-X-MAP) and encryption keys.
final media = playlist as HlsMediaPlaylist;
print('Target Duration: ${media.targetDuration}');
print('Is Live: ${media.isLive}'); // Computed: no ENDLIST and not VOD type
for (var segment in media.segments) {
if (segment.key != null) {
print('Segment is encrypted with ${segment.key!.method}');
}
print('URI: ${segment.uri}');
}
Master Playlist & Multi-Variant Support
Easily filter and select variants based on bandwidth, resolution, or codecs.
// Find the best quality variant
final bestVariant = master.variants.reduce((a, b) => a.bandwidth > b.bandwidth ? a : b);
// Access specific renditions (Audio/Subtitles)
final audioRenditions = master.renditions.where((r) => r.type == HlsMediaType.audio);
Immutability with copyWith
All models are immutable. To modify a playlist (e.g., adding a custom tag or changing the version), use the copyWith pattern.
final modified = mediaPlaylist.copyWith(
version: 6,
playlistType: 'VOD',
hasEndTag: true,
);
📋 Supported Tags
| Category | Tags Supported |
|---|---|
| Playlist | #EXT-X-VERSION, #EXT-X-TARGETDURATION, #EXT-X-MEDIA-SEQUENCE, #EXT-X-PLAYLIST-TYPE, #EXT-X-INDEPENDENT-SEGMENTS, #EXT-X-START |
| Master | #EXT-X-STREAM-INF, #EXT-X-I-FRAME-STREAM-INF, #EXT-X-MEDIA, #EXT-X-SESSION-KEY, #EXT-X-DEFINE |
| Segments | #EXTINF, #EXT-X-BYTERANGE, #EXT-X-DISCONTINUITY, #EXT-X-KEY, #EXT-X-MAP, #EXT-X-PROGRAM-DATE-TIME |
💡 Why use this library?
- Round-Trip Integrity: Parse a manifest, change one value, and compose it back—the structure remains intact.
- Zero Dependencies: Lightweight and fast, utilizing core Dart libraries.
- Error Handling: Throws descriptive
HlsParserExceptionorFormatExceptionfor invalid manifests rather than failing silently.