m3u8 parser for rust
Go to file
2022-04-08 16:27:04 -07:00
.github/workflows Check cargo fmt on CI 2021-10-19 19:48:26 +02:00
examples Re-export all types from the crate root and remove the playlist sub-module 2021-11-18 15:00:01 +02:00
sample-playlists Require each M3U8 playlist to start with the #EXTM3U8 tag 2021-11-17 19:14:58 +02:00
src #EXT-X-ENDLIST is moved to be the last part of the media manifest. Theoretically it can appear anywhere in manifest, so the current placement is not breaking the standard, but not usually is what found in the wild and I believe will help with readability. As any placement is possible, the placement at the end is completely legal. 2022-04-08 16:27:04 -07:00
tests Re-export all types from the crate root and remove the playlist sub-module 2021-11-18 15:00:01 +02:00
.gitignore Version 1.0.0 2016-06-03 20:56:45 +02:00
Cargo.toml Update version to 3.0.0 2021-11-18 15:05:32 +02:00
LICENSE LICENSE 2016-06-03 21:25:34 +02:00
masterplaylist.m3u8 Version 1.0.0 2016-06-03 20:56:45 +02:00
mediaplaylist.m3u8 Version 1.0.0 2016-06-03 20:56:45 +02:00
playlist.m3u8 Version 1.0.0 2016-06-03 20:56:45 +02:00
README.md Use u64 instead of i32 for byte ranges and sequence numbers 2022-01-07 12:47:40 +02:00

m3u8-rs

crates.io

A Rust library for parsing m3u8 playlists (HTTP Live Streaming) link. Uses the nom library for all of the parsing.

Installation

To use this library, add the following dependency to Cargo.toml:

[dependencies]
m3u8-rs = "1.0.6"

Also available on crates.io

Documentation

Available here

Examples

A simple example of parsing a playlist:

use m3u8_rs::playlist::Playlist;
use std::io::Read;

let mut file = std::fs::File::open("playlist.m3u8").unwrap();
let mut bytes: Vec<u8> = Vec::new();
file.read_to_end(&mut bytes).unwrap();

let parsed = m3u8_rs::parse_playlist_res(&bytes);

match parsed {
    Ok(Playlist::MasterPlaylist(pl)) => println!("Master playlist:\n{}", pl),
    Ok(Playlist::MediaPlaylist(pl)) => println!("Media playlist:\n{}", pl),
    Err(e) => println!("Error: {:?}", e)
}

In the example above, parse_playlist_res(&bytes) returns a Result<Playlist, IResult>. It uses the output of parse_playlist(&bytes) behind the scenes and just converts the IResult to a Result. Here is an example of using the parse_playlist(&bytes) with IResult directly:

use m3u8_rs::playlist::Playlist;
use std::io::Read;
use nom::IResult;

let mut file = std::fs::File::open("playlist.m3u8").unwrap();
let mut bytes: Vec<u8> = Vec::new();
file.read_to_end(&mut bytes).unwrap();

let parsed = m3u8_rs::parse_playlist(&bytes);

match parsed {
    IResult::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{}", pl),
    IResult::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{}", pl),
    IResult::Error(e) =>  panic!("Parsing error: \n{}", e),
    IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e),
}

Currently, the parser will succeed even if REQUIRED attributes/tags are missing from a playlist (such as the #EXT-X-VERSION tag). The option to abort parsing when attributes/tags are missing may be something to add later on.

Structure Summary

All the details about the structs are taken from https://tools.ietf.org/html/draft-pantos-http-live-streaming-19.


// Short summary of the important structs in playlist.rs:
//
pub enum Playlist {
    MasterPlaylist(MasterPlaylist),
    MediaPlaylist(MediaPlaylist),
}

pub struct MasterPlaylist {
    pub version: usize,
    pub variants: Vec<VariantStream>,
    pub session_data: Option<SessionData>,
    pub session_key: Option<SessionKey>,
    pub start: Option<Start>,
    pub independent_segments: bool,
    pub alternatives: Vec<AlternativeMedia>,
    pub unknown_tags: Vec<ExtTag>,
}

pub struct MediaPlaylist {
    pub version: usize,
    pub target_duration: f32,
    pub media_sequence: u64,
    pub segments: Vec<MediaSegment>,
    pub discontinuity_sequence: u64,
    pub end_list: bool,
    pub playlist_type: MediaPlaylistType,
    pub i_frames_only: bool,
    pub start: Option<Start>,
    pub independent_segments: bool,
}

pub struct VariantStream {
    pub is_i_frame: bool,
    pub uri: String,
    pub bandwidth: String,
    pub average_bandwidth: Option<String>,
    pub codecs: String,
    pub resolution: Option<String>,
    pub frame_rate: Option<String>,
    pub audio: Option<String>,
    pub video: Option<String>,
    pub subtitles: Option<String>,
    pub closed_captions: Option<String>,
    pub alternatives: Vec<AlternativeMedia>,
}

pub struct MediaSegment {
    pub uri: String,
    pub duration: f32,
    pub title: Option<String>,
    pub byte_range: Option<ByteRange>,
    pub discontinuity: bool,
    pub key: Option<Key>,
    pub map: Option<Map>,
    pub program_date_time: Option<String>,
    pub daterange: Option<String>,
    pub unknown_tags: Vec<ExtTag>,
}