mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-11-08 21:24:56 -05:00
UI: improve aspect ratio handling
As written in the changelog: Live streams formerly worked around a Firefox pixel aspect ratio bug by forcing all videos to 16:9, which dramatically distorted 9:16 camera views. Playback didn't, so anamorphic videos looked correct on Chrome but slightly stretched on Firefox. Now both live streams and playback are fully correct on all browsers.
This commit is contained in:
1
server/Cargo.lock
generated
1
server/Cargo.lock
generated
@@ -1264,6 +1264,7 @@ dependencies = [
|
||||
"moonfire-base",
|
||||
"mylog",
|
||||
"nix 0.22.0",
|
||||
"num-rational 0.4.0",
|
||||
"odds",
|
||||
"parking_lot",
|
||||
"pretty-hex",
|
||||
|
||||
@@ -29,6 +29,7 @@ libpasta = "0.1.2"
|
||||
log = "0.4"
|
||||
mylog = { git = "https://github.com/scottlamb/mylog" }
|
||||
nix = "0.22.0"
|
||||
num-rational = { version = "0.4.0", default-features = false, features = ["std"] }
|
||||
odds = { version = "0.4.0", features = ["std-vec"] }
|
||||
parking_lot = { version = "0.11.1", features = [] }
|
||||
pretty-hex = "0.2.1"
|
||||
|
||||
@@ -135,6 +135,16 @@ pub struct VideoSampleEntry {
|
||||
pub pasp_v_spacing: u16,
|
||||
}
|
||||
|
||||
impl VideoSampleEntry {
|
||||
/// Returns the aspect ratio as a minimized ratio.
|
||||
pub fn aspect(&self) -> num_rational::Ratio<u32> {
|
||||
num_rational::Ratio::new(
|
||||
u32::from(self.width) * u32::from(self.pasp_h_spacing),
|
||||
u32::from(self.height) * u32::from(self.pasp_v_spacing),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct VideoSampleEntryToInsert {
|
||||
pub data: Vec<u8>,
|
||||
|
||||
@@ -496,15 +496,20 @@ pub struct VideoSampleEntry {
|
||||
pub height: u16,
|
||||
pub pasp_h_spacing: u16,
|
||||
pub pasp_v_spacing: u16,
|
||||
pub aspect_width: u32,
|
||||
pub aspect_height: u32,
|
||||
}
|
||||
|
||||
impl VideoSampleEntry {
|
||||
fn from(e: &db::VideoSampleEntry) -> Self {
|
||||
let aspect = e.aspect();
|
||||
Self {
|
||||
width: e.width,
|
||||
height: e.height,
|
||||
pasp_h_spacing: e.pasp_h_spacing,
|
||||
pasp_v_spacing: e.pasp_v_spacing,
|
||||
aspect_width: *aspect.numer(),
|
||||
aspect_height: *aspect.denom(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1928,6 +1928,15 @@ impl http_serve::Entity for File {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.0.type_ == Type::InitSegment {
|
||||
// FileBuilder::build() should have failed if there were no video_sample_entries.
|
||||
let ent = self.0.video_sample_entries.first().expect("no video_sample_entries");
|
||||
let aspect = ent.aspect();
|
||||
hdrs.insert("X-Aspect",
|
||||
HeaderValue::try_from(
|
||||
format!("{}:{}", aspect.numer(), aspect.denom())
|
||||
).expect("no invalid chars in X-Aspect format")
|
||||
);
|
||||
}
|
||||
}
|
||||
fn last_modified(&self) -> Option<SystemTime> {
|
||||
@@ -2690,6 +2699,9 @@ mod tests {
|
||||
let mp4 = builder
|
||||
.build(db.db.clone(), db.dirs_by_stream_id.clone())
|
||||
.unwrap();
|
||||
let mut hdrs = http::header::HeaderMap::new();
|
||||
mp4.add_headers(&mut hdrs);
|
||||
assert_eq!(hdrs.get("X-Aspect").unwrap(), "16:9");
|
||||
traverse(mp4.clone()).await;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user