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:
Scott Lamb
2021-08-12 13:32:01 -07:00
parent 115b081dcd
commit 27395ecd4e
13 changed files with 169 additions and 70 deletions

1
server/Cargo.lock generated
View File

@@ -1264,6 +1264,7 @@ dependencies = [
"moonfire-base",
"mylog",
"nix 0.22.0",
"num-rational 0.4.0",
"odds",
"parking_lot",
"pretty-hex",

View File

@@ -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"

View File

@@ -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>,

View File

@@ -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(),
}
}
}

View File

@@ -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;
}