Skip to content

Commit

Permalink
Merge pull request #10 from rerun-io/emilk/better-info
Browse files Browse the repository at this point in the history
Add helper functions for codec string and bit depth
  • Loading branch information
emilk authored Oct 8, 2024
2 parents 4705e85 + fc35049 commit 8614aae
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 102 deletions.
2 changes: 1 addition & 1 deletion src/mp4box/av01.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct Av01Box {
#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub depth: u16, // This is usually 24, even for HDR with bit_depth=10
pub av1c: RawBox<Av1CBox>,
}

Expand Down
2 changes: 1 addition & 1 deletion src/mp4box/avc1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct Avc1Box {
#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub depth: u16, // This is usually 24, even for HDR with bit_depth=10
pub avcc: RawBox<AvcCBox>,
}

Expand Down
2 changes: 1 addition & 1 deletion src/mp4box/hevc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct HevcBox {
#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub depth: u16, // This is usually 24, even for HDR with bit_depth=10
pub hvcc: RawBox<HevcDecoderConfigurationRecord>,
}

Expand Down
120 changes: 120 additions & 0 deletions src/mp4box/stsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,126 @@ impl Default for StsdBoxContent {
}
}

impl StsdBoxContent {
/// Per color component bit depth.
///
/// Usually 8, but 10 for HDR (for example).
pub fn bit_depth(&self) -> Option<u8> {
#[allow(clippy::match_same_arms)]
match self {
Self::Av01(bx) => Some(bx.av1c.bit_depth),

Self::Avc1(_) => None, // TODO(emilk): figure out bit depth

Self::Hvc1(_) => None, // TODO(emilk): figure out bit depth

Self::Hev1(_) => None, // TODO(emilk): figure out bit depth

Self::Vp08(bx) => Some(bx.vpcc.bit_depth),

Self::Vp09(bx) => Some(bx.vpcc.bit_depth),

Self::Mp4a(_) | Self::Tx3g(_) | Self::Unknown(_) => None, // Not applicable
}
}

pub fn codec_string(&self) -> Option<String> {
Some(match self {
Self::Av01(Av01Box { av1c, .. }) => {
let profile = av1c.profile;
let level = av1c.level;
let tier = if av1c.tier == 0 { "M" } else { "H" };
let bit_depth = av1c.bit_depth;

format!("av01.{profile}.{level:02}{tier}.{bit_depth:02}")
}

Self::Avc1(Avc1Box { avcc, .. }) => {
let profile = avcc.avc_profile_indication;
let constraint = avcc.profile_compatibility;
let level = avcc.avc_level_indication;

// https://aomediacodec.github.io/av1-isobmff/#codecsparam
format!("avc1.{profile:02X}{constraint:02X}{level:02X}")
}

Self::Hvc1(HevcBox { hvcc, .. }) => {
format!("hvc1{}", hevc_codec_details(hvcc))
}

Self::Hev1(HevcBox { hvcc, .. }) => {
format!("hev1{}", hevc_codec_details(hvcc))
}

Self::Vp08(Vp08Box { vpcc, .. }) => {
let profile = vpcc.profile;
let level = vpcc.level;
let bit_depth = vpcc.bit_depth;

format!("vp08.{profile:02}.{level:02}.{bit_depth:02}")
}

Self::Vp09(Vp09Box { vpcc, .. }) => {
let profile = vpcc.profile;
let level = vpcc.level;
let bit_depth = vpcc.bit_depth;

format!("vp09.{profile:02}.{level:02}.{bit_depth:02}")
}

Self::Mp4a(_) | Self::Tx3g(_) | Self::Unknown(_) => return None,
})
}
}

fn hevc_codec_details(hvcc: &crate::hevc::HevcDecoderConfigurationRecord) -> String {
use std::fmt::Write as _;

let mut codec = String::new();
match hvcc.general_profile_space {
1 => codec.push_str(".A"),
2 => codec.push_str(".B"),
3 => codec.push_str(".C"),
_ => {}
}
write!(&mut codec, ".{}", hvcc.general_profile_idc).ok();

let mut val = hvcc.general_profile_compatibility_flags;
let mut reversed = 0;
for i in 0..32 {
reversed |= val & 1;
if i == 31 {
break;
}
reversed <<= 1;
val >>= 1;
}
write!(&mut codec, ".{reversed:X}").ok();

if hvcc.general_tier_flag {
codec.push_str(".H");
} else {
codec.push_str(".L");
}
write!(&mut codec, "{}", hvcc.general_level_idc).ok();

let mut constraint = [0u8; 6];
constraint.copy_from_slice(&hvcc.general_constraint_indicator_flag.to_be_bytes()[2..]);
let mut has_byte = false;
let mut i = 5isize;
while 0 <= i {
let v = constraint[i as usize];
if v > 0 || has_byte {
write!(&mut codec, ".{v:00X}").ok();
has_byte = true;
}
i -= 1;
}

codec
}

/// Information about the video codec.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct StsdBox {
pub version: u8,
Expand Down
2 changes: 1 addition & 1 deletion src/mp4box/vp08.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub struct Vp08Box {
pub reserved1: [u8; 4],
pub frame_count: u16,
pub compressorname: [u8; 32],
pub depth: u16,
pub depth: u16, // This is usually 24, even for HDR with bit_depth=10
pub end_code: u16,
pub vpcc: RawBox<VpccBox>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/mp4box/vp09.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct Vp09Box {
pub reserved1: [u8; 4],
pub frame_count: u16,
pub compressorname: [u8; 32],
pub depth: u16,
pub depth: u16, // This is usually 24, even for HDR with bit_depth=10
pub end_code: u16,
pub vpcc: RawBox<VpccBox>,
}
Expand Down
100 changes: 3 additions & 97 deletions src/reader.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::collections::BTreeMap;
use std::fmt::Write as _;
use std::io::SeekFrom;
use std::io::{Read, Seek};

use crate::{
skip_box, Av01Box, Avc1Box, BoxHeader, BoxType, EmsgBox, Error, FtypBox, HevcBox, MoofBox,
MoovBox, ReadBox, Result, StblBox, StsdBoxContent, TfhdBox, TrackId, TrackKind, TrakBox,
TrunBox, Vp08Box, Vp09Box,
skip_box, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, ReadBox, Result,
StblBox, StsdBoxContent, TfhdBox, TrackId, TrackKind, TrakBox, TrunBox,
};

#[derive(Debug)]
Expand Down Expand Up @@ -497,100 +495,8 @@ impl Track {
}

pub fn codec_string(&self, mp4: &Mp4) -> Option<String> {
let sample_description = &self.trak(mp4).mdia.minf.stbl.stsd;

Some(match &sample_description.contents {
StsdBoxContent::Av01(Av01Box { av1c, .. }) => {
let profile = av1c.profile;
let level = av1c.level;
let tier = if av1c.tier == 0 { "M" } else { "H" };
let bit_depth = av1c.bit_depth;

format!("av01.{profile}.{level:02}{tier}.{bit_depth:02}")
}

StsdBoxContent::Avc1(Avc1Box { avcc, .. }) => {
let profile = avcc.avc_profile_indication;
let constraint = avcc.profile_compatibility;
let level = avcc.avc_level_indication;

format!("avc1.{profile:02X}{constraint:02X}{level:02X}")
}

StsdBoxContent::Hvc1(HevcBox { hvcc, .. }) => {
format!("hvc1{}", hevc_codec_details(hvcc))
}

StsdBoxContent::Hev1(HevcBox { hvcc, .. }) => {
format!("hev1{}", hevc_codec_details(hvcc))
}

StsdBoxContent::Vp08(Vp08Box { vpcc, .. }) => {
let profile = vpcc.profile;
let level = vpcc.level;
let bit_depth = vpcc.bit_depth;

format!("vp08.{profile:02}.{level:02}.{bit_depth:02}")
}

StsdBoxContent::Vp09(Vp09Box { vpcc, .. }) => {
let profile = vpcc.profile;
let level = vpcc.level;
let bit_depth = vpcc.bit_depth;

format!("vp09.{profile:02}.{level:02}.{bit_depth:02}")
}

StsdBoxContent::Mp4a(_) | StsdBoxContent::Tx3g(_) | StsdBoxContent::Unknown(_) => {
return None
}
})
}
}

fn hevc_codec_details(hvcc: &crate::hevc::HevcDecoderConfigurationRecord) -> String {
let mut codec = String::new();
match hvcc.general_profile_space {
1 => codec.push_str(".A"),
2 => codec.push_str(".B"),
3 => codec.push_str(".C"),
_ => {}
self.trak(mp4).mdia.minf.stbl.stsd.contents.codec_string()
}
write!(&mut codec, ".{}", hvcc.general_profile_idc).ok();

let mut val = hvcc.general_profile_compatibility_flags;
let mut reversed = 0;
for i in 0..32 {
reversed |= val & 1;
if i == 31 {
break;
}
reversed <<= 1;
val >>= 1;
}
write!(&mut codec, ".{reversed:X}").ok();

if hvcc.general_tier_flag {
codec.push_str(".H");
} else {
codec.push_str(".L");
}
write!(&mut codec, "{}", hvcc.general_level_idc).ok();

let mut constraint = [0u8; 6];
constraint.copy_from_slice(&hvcc.general_constraint_indicator_flag.to_be_bytes()[2..]);
let mut has_byte = false;
let mut i = 5isize;
while i >= 0 {
let v = constraint[i as usize];
if v > 0 || has_byte {
write!(&mut codec, ".{v:00X}").ok();
has_byte = true;
}
i -= 1;
}

codec
}

#[derive(Default, Clone, Copy)]
Expand Down

0 comments on commit 8614aae

Please sign in to comment.