Skip to content

Commit

Permalink
isomp4: Add HEVC basic detection
Browse files Browse the repository at this point in the history
  • Loading branch information
sscobici committed Oct 4, 2024
1 parent 0c9f1f7 commit 5afcb1a
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 5 deletions.
2 changes: 0 additions & 2 deletions symphonia-format-isomp4/src/atoms/avcc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ impl Atom for AvcCAtom {

let extra_data = reader.read_boxed_slice_exact(len as usize)?;

dbg!(extra_data.len());

// Parse the AVCDecoderConfigurationRecord to get the profile and level. Defined in
// ISO/IEC 14496-15 section 5.3.3.1.
let mut br = BitReaderLtr::new(&extra_data);
Expand Down
72 changes: 72 additions & 0 deletions symphonia-format-isomp4/src/atoms/hvcc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Symphonia
// Copyright (c) 2019-2022 The Project Symphonia Developers.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use symphonia_core::codecs::video::well_known::CODEC_ID_HEVC;
use symphonia_core::codecs::video::VideoCodecParameters;
use symphonia_core::codecs::CodecProfile;
use symphonia_core::errors::{decode_error, Error, Result};
use symphonia_core::io::{BitReaderLtr, ReadBitsLtr, ReadBytes};

use crate::atoms::{Atom, AtomHeader};

#[allow(dead_code)]
#[derive(Debug)]
pub struct HvcCAtom {
/// HEVC extra data (HEVCDecoderConfigurationRecord).
extra_data: Box<[u8]>,
profile: CodecProfile,
level: u32,
}

impl Atom for HvcCAtom {
fn read<B: ReadBytes>(reader: &mut B, header: AtomHeader) -> Result<Self> {
// The HEVCConfiguration atom payload is a single HEVCDecoderConfigurationRecord. This record
// forms the defacto codec extra data.
let len = header
.data_len()
.ok_or_else(|| Error::DecodeError("isomp4 (hvcC): expected atom size to be known"))?;

let extra_data = reader.read_boxed_slice_exact(len as usize)?;

// Parse the HEVCDecoderConfigurationRecord to get the profile and level. Defined in
// ISO/IEC 14496-15 section 8.3.3.1.2
let mut br = BitReaderLtr::new(&extra_data);

// Configuration version is always 1.
let configuration_version = br.read_bits_leq32(8)?;

if configuration_version != 1 {
return decode_error(
"isomp4 (hvcC): unexpected hevc decoder configuration record version",
);
}

// HEVC profile as defined in ISO/IEC 23008-2.
let _general_profile_space = br.read_bits_leq32(2)?;
let _general_tier_flag = br.read_bits_leq32(1)?;
let general_profile_idc = br.read_bits_leq32(5)?;
let _general_profile_compatibility_flags = br.read_bits_leq32(32)?;
let _general_constraint_indicator_flags = br.read_bits_leq64(48)?;
let general_level_idc = br.read_bits_leq32(8)?;

Ok(Self {
extra_data,
profile: CodecProfile::new(general_profile_idc),
level: general_level_idc,
})
}
}

impl HvcCAtom {
pub fn fill_codec_params(&self, codec_params: &mut VideoCodecParameters) {
codec_params
.for_codec(CODEC_ID_HEVC)
.with_profile(self.profile)
.with_level(self.level)
.with_extra_data(self.extra_data.clone());
}
}
10 changes: 10 additions & 0 deletions symphonia-format-isomp4/src/atoms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub(crate) mod esds;
pub(crate) mod flac;
pub(crate) mod ftyp;
pub(crate) mod hdlr;
pub(crate) mod hvcc;
pub(crate) mod ilst;
pub(crate) mod mdhd;
pub(crate) mod mdia;
Expand Down Expand Up @@ -62,6 +63,7 @@ pub use esds::EsdsAtom;
pub use flac::FlacAtom;
pub use ftyp::FtypAtom;
pub use hdlr::HdlrAtom;
pub use hvcc::HvcCAtom;
pub use ilst::IlstAtom;
pub use mdhd::MdhdAtom;
pub use mdia::MdiaAtom;
Expand Down Expand Up @@ -147,6 +149,7 @@ pub enum AtomType {
GroupingTag,
Handler,
HdVideoTag,
HevcConfiguration,
IdentPodcastTag,
KeywordTag,
LongDescriptionTag,
Expand Down Expand Up @@ -209,6 +212,9 @@ pub enum AtomType {
Uuid,
VisualSampleEntryAv1,
VisualSampleEntryAvc1,
VisualSampleEntryDvh1,
VisualSampleEntryDvhe,
VisualSampleEntryHev1,
VisualSampleEntryHvc1,
VisualSampleEntryMp4v,
VisualSampleEntryVp8,
Expand All @@ -233,6 +239,8 @@ impl From<[u8; 4]> for AtomType {
b"data" => AtomType::MetaTagData,
b"dfLa" => AtomType::FlacDsConfig,
b"dOps" => AtomType::OpusDsConfig,
b"dvh1" => AtomType::VisualSampleEntryDvh1,
b"dvhe" => AtomType::VisualSampleEntryDvhe,
b"edts" => AtomType::Edit,
b"elst" => AtomType::EditList,
b"esds" => AtomType::Esds,
Expand All @@ -242,7 +250,9 @@ impl From<[u8; 4]> for AtomType {
b"free" => AtomType::Free,
b"ftyp" => AtomType::FileType,
b"hdlr" => AtomType::Handler,
b"hev1" => AtomType::VisualSampleEntryHev1,
b"hvc1" => AtomType::VisualSampleEntryHvc1,
b"hvcC" => AtomType::HevcConfiguration,
b"ilst" => AtomType::MetaList,
b"in24" => AtomType::AudioSampleEntryS24,
b"in32" => AtomType::AudioSampleEntryS32,
Expand Down
43 changes: 40 additions & 3 deletions symphonia-format-isomp4/src/atoms/stsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use symphonia_core::io::ReadBytes;
use crate::atoms::{AlacAtom, Atom, AtomHeader, AtomType, EsdsAtom, FlacAtom, OpusAtom, WaveAtom};
use crate::fp::FpU16;

use super::{AtomIterator, AvcCAtom};
use super::{AtomIterator, AvcCAtom, HvcCAtom};

/// Sample description atom.
#[allow(dead_code)]
Expand Down Expand Up @@ -76,6 +76,9 @@ impl Atom for StsdAtom {
}
AtomType::VisualSampleEntryAv1
| AtomType::VisualSampleEntryAvc1
| AtomType::VisualSampleEntryDvh1
| AtomType::VisualSampleEntryDvhe
| AtomType::VisualSampleEntryHev1
| AtomType::VisualSampleEntryHvc1
| AtomType::VisualSampleEntryMp4v
| AtomType::VisualSampleEntryVp8
Expand Down Expand Up @@ -148,6 +151,12 @@ impl StsdAtom {
Some(VisualCodecSpecific::Avc1(ref avc)) => {
avc.fill_codec_params(&mut codec_params);
}
Some(VisualCodecSpecific::Dvh1(ref hevc))
| Some(VisualCodecSpecific::Dvhe(ref hevc))
| Some(VisualCodecSpecific::Hev1(ref hevc))
| Some(VisualCodecSpecific::Hvc1(ref hevc)) => {
hevc.fill_codec_params(&mut codec_params);
}
_ => (),
}

Expand Down Expand Up @@ -551,7 +560,10 @@ pub struct VisualSampleEntry {
pub enum VisualCodecSpecific {
Av1,
Avc1(AvcCAtom),
Hvc1,
Dvhe(HvcCAtom),
Dvh1(HvcCAtom),
Hev1(HvcCAtom),
Hvc1(HvcCAtom),
Mp4v,
Vp8,
Vp9,
Expand Down Expand Up @@ -610,11 +622,36 @@ fn read_visual_sample_entry<B: ReadBytes>(
AtomType::AvcConfiguration => {
// AVC
if header.atom_type != AtomType::VisualSampleEntryAvc1 || codec_specific.is_some() {
return decode_error("isomp4: invalid sample entry");
return decode_error("isomp4: invalid avc configuration sample entry");
}

codec_specific = Some(VisualCodecSpecific::Avc1(iter.read_atom::<AvcCAtom>()?));
}
AtomType::HevcConfiguration => {
// HEVC
if (header.atom_type != AtomType::VisualSampleEntryDvhe
&& header.atom_type != AtomType::VisualSampleEntryDvh1
&& header.atom_type != AtomType::VisualSampleEntryHev1
&& header.atom_type != AtomType::VisualSampleEntryHvc1) || codec_specific.is_some() {
return decode_error("isomp4: invalid hevc configuration sample entry");
}

match header.atom_type {
AtomType::VisualSampleEntryDvhe => {
codec_specific = Some(VisualCodecSpecific::Dvhe(iter.read_atom::<HvcCAtom>()?));
}
AtomType::VisualSampleEntryDvh1 => {
codec_specific = Some(VisualCodecSpecific::Dvh1(iter.read_atom::<HvcCAtom>()?));
}
AtomType::VisualSampleEntryHev1 => {
codec_specific = Some(VisualCodecSpecific::Hev1(iter.read_atom::<HvcCAtom>()?));
}
AtomType::VisualSampleEntryHvc1 => {
codec_specific = Some(VisualCodecSpecific::Hvc1(iter.read_atom::<HvcCAtom>()?));
}
_ => {}
}
}
_ => {
debug!("unknown visual sample entry sub-atom: {:?}.", entry_header.atom_type());
}
Expand Down

0 comments on commit 5afcb1a

Please sign in to comment.