Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper functions for codec string and bit depth #10

Merged
merged 6 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, // I don't know what this is, but it is usually 24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this is just bit depth, 3 channels * 8 bits per pixel = 24 bits total

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I was about to write as well, especially since our av1 decoder also likes to talk in "total bits per full color sample" (i.e. 24/30/34). But then Emil told me on a call that he observed 24bit on a 10bit hdr video if I understood correctly.
Maybe worth pointing out here!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get depth: 24, bit_depth: 10 when I print both

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, // I don't know what this is, but it is usually 24
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, // I don't know what this is, but it is usually 24
pub hvcc: RawBox<HevcDecoderConfigurationRecord>,
}

Expand Down
121 changes: 121 additions & 0 deletions src/mp4box/stsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use crate::mp4box::{
};

/// Codec dependent contents of the stsd box.
///
/// <https://developer.apple.com/documentation/quicktime-file-format/sample_description_atom>
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum StsdBoxContent {
/// AV1 video codec
Expand Down Expand Up @@ -51,6 +53,125 @@ impl Default for StsdBoxContent {
}
}

impl StsdBoxContent {
/// 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;

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.
///
/// <https://developer.apple.com/documentation/quicktime-file-format/sample_description_atom>
#[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, // I don't know what this is, but it is usually 24
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, // I don't know what this is, but it is usually 24
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
Loading