SubtitleTrack now carries the codec, split text formats from bitmap ones per DESIGN.md §15, plus the forced and SDH dispositions ffprobe reports. Without the forced flag a file carrying only a forced track read as satisfied for that language.
93 lines
2.7 KiB
Rust
93 lines
2.7 KiB
Rust
//! The slice of `ffprobe`'s JSON output this crate reads.
|
|
//!
|
|
//! Everything is optional because `ffprobe` omits fields rather than nulling
|
|
//! them, and which ones it omits depends on the container.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use serde::Deserialize;
|
|
|
|
/// One `ffprobe -print_format json -show_format -show_streams` document.
|
|
#[derive(Debug, Deserialize)]
|
|
pub(crate) struct Output {
|
|
#[serde(default)]
|
|
pub format: Format,
|
|
#[serde(default)]
|
|
pub streams: Vec<Stream>,
|
|
}
|
|
|
|
/// Container-level facts.
|
|
#[derive(Debug, Default, Deserialize)]
|
|
pub(crate) struct Format {
|
|
#[serde(rename = "format_name")]
|
|
pub name: Option<String>,
|
|
pub duration: Option<String>,
|
|
pub size: Option<String>,
|
|
}
|
|
|
|
/// One stream of any kind. `codec_type` says which.
|
|
#[derive(Debug, Deserialize)]
|
|
pub(crate) struct Stream {
|
|
pub codec_type: Option<String>,
|
|
pub codec_name: Option<String>,
|
|
pub width: Option<u32>,
|
|
pub height: Option<u32>,
|
|
pub color_transfer: Option<String>,
|
|
pub duration: Option<String>,
|
|
#[serde(default)]
|
|
pub disposition: Disposition,
|
|
#[serde(default)]
|
|
pub tags: Tags,
|
|
#[serde(default)]
|
|
pub side_data_list: Vec<SideData>,
|
|
}
|
|
|
|
impl Stream {
|
|
pub fn is_kind(&self, kind: &str) -> bool {
|
|
self.codec_type.as_deref() == Some(kind)
|
|
}
|
|
|
|
/// A real video stream, not the cover art bolted onto an audio file.
|
|
pub fn is_video(&self) -> bool {
|
|
self.is_kind("video") && self.disposition.attached_pic == 0
|
|
}
|
|
}
|
|
|
|
/// Stream flags. `attached_pic` stops an MP3 or FLAC's embedded artwork
|
|
/// passing for video; `forced` and `hearing_impaired` are §15's track
|
|
/// dispositions.
|
|
#[derive(Debug, Default, Deserialize)]
|
|
pub(crate) struct Disposition {
|
|
#[serde(default)]
|
|
pub attached_pic: u8,
|
|
#[serde(default)]
|
|
pub forced: u8,
|
|
#[serde(rename = "hearing_impaired", default)]
|
|
pub hearing_impaired: u8,
|
|
}
|
|
|
|
/// Stream metadata. Matroska writes `HANDLER_NAME`, MP4 writes `handler_name`,
|
|
/// so every lookup here is case-insensitive.
|
|
#[derive(Debug, Default, Deserialize)]
|
|
pub(crate) struct Tags(BTreeMap<String, String>);
|
|
|
|
impl Tags {
|
|
pub fn get(&self, key: &str) -> Option<&str> {
|
|
self.0
|
|
.iter()
|
|
.find(|(name, _)| name.eq_ignore_ascii_case(key))
|
|
.map(|(_, value)| value.as_str())
|
|
.filter(|value| !value.is_empty())
|
|
}
|
|
}
|
|
|
|
/// A stream side-data block. Only the Dolby Vision configuration record and
|
|
/// the HDR10+ marker are read; the rest are ignored.
|
|
#[derive(Debug, Deserialize)]
|
|
pub(crate) struct SideData {
|
|
#[serde(rename = "side_data_type")]
|
|
pub kind: Option<String>,
|
|
pub dv_profile: Option<u8>,
|
|
pub dv_bl_signal_compatibility_id: Option<u8>,
|
|
}
|