emarsden / dash-mpd-rs

Rust library for parsing, serializing and downloading media content from a DASH MPD manifest.
MIT License
72 stars 23 forks source link
dash mpeg mpeg-dash rust streaming-video

dash-mpd

A Rust library for parsing, serializing and downloading media content from a DASH MPD file, as used by video services such as on-demand replay of TV content and video streaming services like YouTube. Allows both parsing of a DASH manifest (XML format) to Rust structs (deserialization) and programmatic generation of an MPD manifest (serialization). The library also allows you to download media content from a streaming server.

Crates.io Released API docs CI Dependency status Recent crates.io downloads LICENSE

DASH (dynamic adaptive streaming over HTTP), also called MPEG-DASH, is a technology used for media streaming over the web, commonly used for video on demand (VOD) services. The Media Presentation Description (MPD) is a description of the resources (manifest or “playlist”) forming a streaming service, that a DASH client uses to determine which assets to request in order to perform adaptive streaming of the content. DASH MPD manifests can be used both with content encoded as H.264/MPEG and as WebM, and with file segments using either MPEG-2 Transport Stream (M2TS) container format or fragmented MPEG-4 (also called CFF). There is a good explanation of adaptive bitrate video streaming at howvideo.works.

This library provides a serde-based parser (deserializer) and serializer for the DASH MPD format, as formally defined in ISO/IEC standard 23009-1:2019. XML schema files are available for no cost from ISO. The library also provides non-exhaustive support for certain DASH extensions such as the DVB-DASH and HbbTV (Hybrid Broadcast Broadband TV) profiles. When MPD files in practical use diverge from the formal standard(s), this library prefers to interoperate with existing practice.

If the library feature fetch is enabled (which it is by default), the library also provides support for downloading content (audio or video) described by an MPD manifest. This involves selecting the alternative with the most appropriate encoding (in terms of bitrate, codec, etc.), fetching segments of the content using HTTP or HTTPS requests (this functionality depends on the reqwest crate) and muxing audio and video segments together.

Muxing (merging audio and video streams, which are often published separately in DASH media streams) is implemented by calling an external commandline application, either mkvmerge (from the MkvToolnix suite), ffmpeg, vlc or MP4Box. The choice of external muxer depends on the filename extension of the path supplied to download_to() (will be .mp4 if you call download()):

You can specify a different order of preference for muxing applications using the with_muxer_preference method on DashDownloader. For example, with_muxer_preference("avi", "vlc,ffmpeg") means that for an AVI media container the external muxer vlc will be tried first, then ffmpeg in case of failure. This method option can be used multiple times to specify options for different container types.

If the library feature libav is enabled, muxing is implemented using ffmpeg’s libav library, via the ac_ffmpeg crate. This allows the library to work with fewer runtime dependencies. However, these commandline applications implement a number of checks and workarounds to fix invalid input streams that tend to exist in the wild. Some of these workarounds are implemented here when using libav as a library, but not all of them, so download support tends to be more robust with the default configuration (using an external application as a subprocess).

DASH features supported

Limitations / unsupported features

Priority of different stream preference options

The library allows you to express a preference ordering for several characteristics of streams in a DASH manifest (audio language, video resolution, bandwidth/quality, role label). The list belows specifies the order in which these preferences are handled:

Usage

To parse (deserialize) the contents of an MPD manifest into Rust structs:

use std::time::Duration;
use dash_mpd::{MPD, parse};

fn main() {
    let client = reqwest::blocking::Client::builder()
        .timeout(Duration::new(30, 0))
        .build()
        .expect("creating HTTP client");
    let xml = client.get("https://rdmedia.bbc.co.uk/testcard/vod/manifests/avc-ctv-stereo-en.mpd")
        .header("Accept", "application/dash+xml,video/vnd.mpeg.dash.mpd")
        .send()
        .expect("requesting MPD content")
        .text()
        .expect("fetching MPD content");
    let mpd: MPD = parse(&xml)
        .expect("parsing MPD");
    if let Some(pi) = mpd.ProgramInformation {
        if let Some(title) = pi.Title {
            println!("Title: {:?}", title.content);
        }
        if let Some(source) = pi.Source {
            println!("Source: {:?}", source.content);
        }
    }
    for p in mpd.periods {
        if let Some(d) = p.duration {
            println!("Contains Period of duration {d:?}");
        }
    }
}

See example dash_stream_info.rs for more information.

To generate an MPD manifest programmatically:

use dash_mpd::{MPD, ProgramInformation, Title};

fn main() {
   let pi = ProgramInformation {
       Title: Some(Title { content: Some("My serialization example".into()) }),
       lang: Some("eng".into()),
       moreInformationURL: Some("https://github.com/emarsden/dash-mpd-rs".into()),
       ..Default::default()
   };
   let mpd = MPD {
       mpdtype: Some("static".into()),
       xmlns: Some("urn:mpeg:dash:schema:mpd:2011".into()),
       ProgramInformation: Some(pi),
       ..Default::default()
   };

   let xml = mpd.to_string();
}

See example serialize.rs for more detail.

To download content from an MPD manifest:

use dash_mpd::fetch::DashDownloader;

let url = "https://storage.googleapis.com/shaka-demo-assets/heliocentrism/heliocentrism.mpd";
match DashDownloader::new(url)
       .worst_quality()
       .download().await
{
   Ok(path) => println!("Downloaded to {path:?}"),
   Err(e) => eprintln!("Download failed: {e:?}"),
}

See example download_bbc.rs for a little more detail.

An application that provides a convenient commandline interface for the download functionality is available separately in the dash-mpd-cli crate.

Installation

Add to your Cargo.toml file:

[dependencies]
dash-mpd = "0.17.2"

If you don’t need the download functionality and wish to reduce code size, use:

[dependencies]
dash-mpd = { version = "0.17.2", default-features = false }

We endeavour to use semantic versioning for this crate despite its 0.x version number: a major change which requires users of the library to change their code (such as a change in an attribute name or type) will be published in a major release. For a version number 0.y.z, a major release implies a change to y.

Optional features

The following additive Cargo features can be enabled:

Platforms

This crate is tested on the following platforms:

Why?

This library was developed to allow the author to watch a news programme produced by a public media broadcaster whilst at the gym. The programme is published as a DASH stream on the broadcaster’s “replay” service, but network service at the gym is sometimes poor. First world problems!

[!WARNING] The author is not the morality police nor a lawyer, but please note that redistributing media content that you have not produced may, depending on the publication licence, be a breach of intellectual property laws. Also, circumventing DRM may be prohibited in some countries.

License

This project is licensed under the MIT license. For more information, see the LICENSE-MIT file.

Patches and pull requests are welcome.