#video-streaming #mpd #dash #streaming #mpeg #audio-video

dash-mpd

解析、序列化、下载用于MPEG-DASH或WebM-DASH媒体流式的MPD清单

51次发布

0.17.0 2024年8月22日
0.16.6 2024年7月27日
0.16.5 2024年6月22日
0.16.0 2024年3月30日
0.1.0 2021年11月28日

#2视频

Download history 1237/week @ 2024-05-02 1458/week @ 2024-05-09 1742/week @ 2024-05-16 1941/week @ 2024-05-23 1065/week @ 2024-05-30 1237/week @ 2024-06-06 1195/week @ 2024-06-13 1468/week @ 2024-06-20 1247/week @ 2024-06-27 1135/week @ 2024-07-04 1117/week @ 2024-07-11 1205/week @ 2024-07-18 1839/week @ 2024-07-25 1158/week @ 2024-08-01 1473/week @ 2024-08-08 819/week @ 2024-08-15

5,493 每月下载量
用于 5 个

MIT 许可证

420KB
7K SLoC

dash-mpd

一个Rust库,用于解析、序列化和从DASH MPD文件中下载媒体内容,如视频服务中的电视节目点播和YouTube等视频流服务所使用的。它允许解析DASH清单(XML格式)到Rust结构体(反序列化)以及程序化生成MPD清单(序列化)。该库还允许您从流媒体服务器下载媒体内容。

Crates.io Released API docs CI Dependency status LICENSE

DASH(通过HTTP的动态自适应流),也称为MPEG-DASH,是一种用于网络媒体流的技术,常用于视频点播(VOD)服务。媒体呈现描述(MPD)是构成流媒体服务的资源(清单或“播放列表”)的描述,DASH客户端使用它来确定请求哪些资源以执行内容的自适应流。DASH MPD清单可以与H.264/MPEG编码的内容以及WebM一起使用,以及使用MPEG-2传输流(M2TS)容器格式或分片MPEG-4(也称为CFF)的文件段。关于自适应比特率视频流的好解释可以在 howvideo.works 上找到。

此库提供了一个基于serde的解析器(反序列化器)和序列化器,用于DASH MPD格式,该格式在ISO/IEC标准23009-1:2019中正式定义。XML模式文件可以从 ISO免费获取。该库还提供对某些DASH扩展(如DVB-DASH和HbbTV(混合广播宽带电视)配置文件)的非穷尽支持。当实际使用的MPD文件与正式标准(s)不同时,该库倾向于与现有实践进行互操作。

如果库功能 fetch 被启用(默认情况下是启用的),库还提供对下载由 MPD 清单描述的内容(音频或视频)的支持。这包括选择最适合的编码(从比特率、编解码器等方面考虑),使用 HTTP 或 HTTPS 请求获取内容段(此功能依赖于 reqwest 包)以及将音频和视频段复用在一起。

复用(合并音频和视频流,这些流通常在 DASH 媒体流中单独发布)是通过调用外部命令行应用程序实现的,可以是来自 MkvToolnix 套件的 mkvmerge,ffmpegvlcMP4Box。外部复用器的选择取决于提供给 download_to() 的路径的文件扩展名(如果您调用 download()),它将是 .mp4

  • .mkv:首先调用 mkvmerge,然后如果未安装或失败,则调用 ffmpeg,然后尝试 MP4Box
  • .mp4:首先调用 ffmpeg,如果失败则调用 vlc,然后尝试 MP4Box
  • .webm:先调用 vlc,如果失败则调用 ffmpeg
  • 其他:尝试 ffmpeg,它支持许多容器格式,然后尝试 MP4Box

您可以使用 DashDownloader 上的 with_muxer_preference 方法指定复用应用程序的优先顺序。例如,with_muxer_preference("avi", "vlc,ffmpeg") 表示对于 AVI 媒体容器,将首先尝试外部复用器 vlc,如果失败则尝试 ffmpeg。此方法选项可以多次使用来指定不同容器类型的选项。

如果库功能 libav 被启用,复用是通过 ffmpeg 的 libav 库,通过 ac_ffmpeg 包实现的。这允许库使用更少的运行时依赖项。然而,这些命令行应用程序实现了一些检查和回退来修复在野外存在的无效输入流。当使用 libav 作为库时,这里实现了一些这些回退,但不是全部,因此下载支持通常在默认配置(使用外部应用程序作为子进程)下更稳健。

支持的 DASH 功能

  • 多周期 内容。如果格式兼容(相同分辨率、编解码器、比特率等)并且未在 DashDownloader 上调用 concatenate_periods(false),不同流中的媒体将保存在单个媒体容器中,否则在单独的媒体容器中。

  • WebVTT/wvtt、TTML、STPP、SRT、tx3g 和 SMIL 字幕,可以作为一个单独的媒体流提供,也可以作为分段的 MP4 流提供。以单一媒体流形式分发的字幕将被保存到与请求的输出文件具有相同基本名称的文件中,但文件扩展名与字幕类型相对应(例如,.srt.vtt)。如果已安装,WebVTT/wvtt 格式的字幕(无论是作为单一媒体流还是分段 MP4 流)将使用 MP4Box 命令行工具(来自 GPAC 项目)转换为更标准的 SRT 格式。根据 DASH 规范,STPP 字幕(应格式化为 EBU-TT)将使用 MP4Box(VLC 应该能够读取这些字幕)混合到输出媒体容器中,作为 subt:stpp 流,并且使用 ffmpeg 转换为单独的 .ttml 文件。如果您的媒体播放器不支持 STPP/TTML 字幕,您可以使用带有 gpac -gui 的 GPAC 媒体播放器(。

  • 支持解密使用 MPEG Common Encryption (cenc) 内容保护的媒体流。这需要安装来自 Bento4 套件mp4decrypt 命令行应用程序(通用平台的二进制文件可用),或 Shaka 打包器 应用程序(通用平台的二进制文件作为 GitHub 发布版提供)。请参阅 DashDownloader 上的 add_decryption_key 函数、DashDownloader 上的 with_decryptor_preference 函数以及 decrypt.rs 示例。

  • 包括解析到零的 XLink 元素(仅使用 actuate=onLoad 语义)。

  • 所有形式的段索引信息:SegmentBase@indexRange、SegmentTimeline、SegmentTemplate@duration、SegmentTemplate@index、SegmentList。

  • mkvmerge、ffmpeg、VLC 或 MP4Box 支持的媒体容器类型(这包括 Matroska、ISO-BMFF / CMAF / MP4、WebM、MPEG-2 TS),以及这些应用程序支持的所有编解码器。

局限性/不受支持的特性

  • 我们无法从用于实时流媒体/OTT 电视的 动态 MPD 清单 中下载内容。这是因为我们没有实现所需的时钟功能,以了解何时新的媒体段可用,也没有实现带宽管理功能,允许自适应流媒体。然而,请注意,一些 OTT 提供商对不进行实时直播的内容(即所有媒体段都可用)公开了动态清单,我们可以以“尽可能快”的模式下载这些内容。您可以使用 allow_live_streams() 方法在 DashDownloader 上尝试从这些“伪实时”流中下载。指定 force_duration(secs) 并使用 sleep_between_requests() 也可能很有用,以确保下载速度不超过实时。

    另一种技术是使用 XSLT 样式表 tests/fixtures/rewrite-drop-dynamic.xslt 在下载之前将 dynamic 属性更改为 static,这应该允许您下载此类内容。

  • 不支持具有 actuate=onRequest 语义的 XLink。

  • HLS 流媒体(m3u8 清单)。

  • 微软平滑流媒体。

不同流优先选项的优先级

该库允许您在DASHManifest(音频语言、视频分辨率、带宽/质量、角色标签)中表达对多个流特性的偏好排序。以下列表指定了处理这些偏好的顺序

  • 首先过滤出与我们的语言偏好不对应的Manifest中的AdaptationSets。如果没有指定语言偏好,则不进行过滤。如果有多个AdaptationSets匹配语言偏好,它们都将传递到下一阶段的过滤。

  • 根据角色偏好选择适配。如果没有指定角色偏好,则不根据角色标签进行过滤。如果没有适配匹配我们的任何角色偏好,则不根据角色标签进行过滤。如果至少有一个适配匹配所表达的角色偏好中的一个,则只将位于角色偏好列表首部的适配传递到下一阶段的过滤。

  • 当存在多个Representation元素时,根据任何指定的质量偏好进行过滤。如果没有指定质量偏好,则不进行过滤。过滤基于Representation元素上指定的@qualityRanking属性,如果指定,否则基于指定的@bandwidth属性。请注意,当使用不同的编解码器时,质量排名可能与带宽排名不同。

  • 如果指定了视频宽度偏好,则只选择视频宽度最接近请求宽度的Representation。

  • 如果指定了视频高度偏好,则只选择视频高度最接近请求高度的Representation。

  • 在所有前面的步骤之后,如果有多个流仍然在考虑中,则选择DASHManifest XML中首先出现的第一个流。

用法

将MPDManifest的内容解析(反序列化)到Rust结构体中

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:?}");
        }
    }
}

有关更多信息,请参阅示例dash_stream_info.rs

以编程方式生成MPDManifest

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();
}

有关更多详细信息,请参阅示例serialize.rs

从MPDManifest中下载内容

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:?}"),
}

有关更多详细信息,请参阅示例download_bbc.rs

一个提供方便命令行界面的下载功能的应用程序可以作为dash-mpd-cli crate单独使用。

安装

将其添加到您的Cargo.toml文件中

[dependencies]
dash-mpd = "0.17.0"

如果您不需要下载功能并希望减小代码大小,请使用

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

尽管此crate的版本号为0.x,但我们将努力使用语义版本控制:需要用户更改库中代码的重大更改(如属性名称或类型更改)将在主版本中发布。对于版本号0.y.z,主版本意味着对y的更改。

可选功能

以下可添加的Cargo功能可以启用

  • fetch (默认启用):启用下载流内容的支持。这占用了库的大部分代码大小,因此如果您只需要用于序列化和反序列化MPD的结构定义,请禁用它。

  • socks (默认启用):在reqwest依赖项上启用socks功能,为HTTP/HTTPS请求提供SOCKS5代理支持。

  • compression (默认启用):在reqwest依赖项上启用gzip功能,以启用HTTP/HTTPS请求的gzip压缩和解压缩。

  • native-tls (默认启用):在我们的 reqwest 依赖项上启用原生-tls 功能,以使用平台默认的 TLS 实现来启用 HTTPS 请求。

  • rustls-tls:在我们的 reqwest 依赖项上启用 rustls-tls 功能(使用 rustls 而不是系统原生 TLS)。您可能需要启用此功能(并构建不带 native-tls),以便在 Linux 上与 musl-libc 目标进行静态链接。

  • libav:通过 ac-ffmpeg crate 启用链接到 ffmpeg 的库以支持复用(而不是调用 mkvmerge、ffmpeg 或 vlc 作为子进程)。

  • hickory-dns:在我们的 reqwest 依赖项上启用 hickory-dns 功能,使用 Hickory DNS 解析库 而不是系统解析器。(此功能之前命名为 trust-dns,根据 Hickory DNS 解析器的旧名称。该功能的旧名称仍然被接受,但已弃用。)

  • scte35 (默认启用):启用对应于 SCTE-35 标准的 XML 元素以插入替代内容(主要用于动态插入广告)的支持。

  • warn_ignored_elements:如果启用此功能,在解析 manifest 时,如果 DASH manifest 中存在的 XML 元素没有被反序列化为 Rust 结构体,将发出警告。默认行为是忽略我们未定义 serde 反序列化指令的元素。此功能使用 serde_ignored crate 实现。

平台

此 crate 在以下平台上进行了测试

  • Linux,具有默认功能(使用 mkvmerge、ffmpeg、vlc 或 MP4Box 作为子进程进行复用)和 libav 支持,在 AMD64 和 Aarch64 架构上。这是作者使用的平台,因此是最经过测试的平台。

  • MacOS/Aarch64,没有 libav 功能(在当前 ffmpeg 上构建 ac-ffmpeg crate 时存在问题)

  • Microsoft Windows 10 和 Windows 11,没有 libav 功能

  • 通过 termux 在 Aarch64 上运行 Android 12,没有 libav 功能。您需要安装 rustbinutilsffmpegprotobuf 软件包。

  • FreeBSD/AMD64 和 OpenBSD/AMD64,没有 libav 功能。但是请注意,我们用于复用或解密媒体内容的某些外部实用程序在这些平台上支持不佳。

  • Solaris 11.4 在 AMD64 和 Sparc 上(您可能需要将 CC 设置为 gcc 以构建 protobuf-src crate)。

为什么?

此库的开发是为了让作者在健身房时能够观看由公共媒体广播机构制作的新闻节目。该节目作为广播机构“重播”服务上的 DASH 流发布,但健身房的网络服务有时较差。第一世界的问题!

作者既不是道德警察也不是律师,但请注意,如果您未制作的内容进行分发,可能违反知识产权法。此外,在某些国家/地区,绕过 DRM 可能是禁止的。

许可

本项目采用 MIT 许可证授权。有关更多信息,请参阅 LICENSE-MIT 文件。

欢迎补丁和 pull request。

依赖项

~9–25MB
~407K SLoC