m3u8 parser for rust
Find a file
2021-11-18 14:52:38 +02:00
.github/workflows Check cargo fmt on CI 2021-10-19 19:48:26 +02:00
examples Migrate to Rust 2018 2021-11-17 16:00:23 +02:00
sample-playlists Require each M3U8 playlist to start with the #EXTM3U8 tag 2021-11-17 19:14:58 +02:00
src Use unwrap_or_default() instead of unwrap_or_else(Default::default) 2021-11-18 14:52:38 +02:00
tests Make most internal parser functions private 2021-11-18 14:50:14 +02:00
.gitignore Version 1.0.0 2016-06-03 20:56:45 +02:00
Cargo.toml Migrate to Rust 2018 2021-11-17 16:00:23 +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 Migrate to Rust 2018 2021-11-17 16:00:23 +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: i32,
    pub segments: Vec<MediaSegment>,
    pub discontinuity_sequence: i32,
    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>,
}