Skip to content

Commit 579c1e3

Browse files
committed
Read alpha item
1 parent 2785cb3 commit 579c1e3

File tree

3 files changed

+62
-10
lines changed

3 files changed

+62
-10
lines changed

mp4parse/src/lib.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,11 @@ impl MediaContext {
748748
pub struct AvifContext {
749749
/// The collected data indicated by the `pitm` box, See ISO 14496-12:2015 § 8.11.4
750750
pub primary_item: TryVec<u8>,
751+
/// Associated alpha channel for the primary item, if any
752+
pub alpha_item: Option<TryVec<u8>>,
753+
/// If true, divide RGB values by the alpha value.
754+
/// See `prem` in MIAF § 7.3.5.2
755+
pub premultiplied_alpha: bool,
751756
}
752757

753758
impl AvifContext {
@@ -757,6 +762,8 @@ impl AvifContext {
757762
}
758763

759764
struct AvifMeta {
765+
item_references: TryVec<ItemReferenceEntry>,
766+
properties: TryVec<AssociatedProperty>,
760767
primary_item_id: u32,
761768
iloc_items: TryVec<ItemLocationBoxItem>,
762769
}
@@ -1258,16 +1265,52 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
12581265

12591266
let meta = meta.ok_or(Error::InvalidData("missing meta"))?;
12601267

1268+
let alpha_item_id = meta
1269+
.item_references
1270+
.iter()
1271+
// Auxiliary image for the primary image
1272+
.filter(|iref| {
1273+
iref.to_item_id == meta.primary_item_id
1274+
&& iref.from_item_id != meta.primary_item_id
1275+
&& iref.item_type == b"auxl"
1276+
})
1277+
.map(|iref| iref.from_item_id)
1278+
// which has the alpha property
1279+
.filter(|&item_id| {
1280+
meta.properties.iter().any(|prop| {
1281+
prop.item_id == item_id
1282+
&& match &prop.property {
1283+
ImageProperty::AuxiliaryType(urn) => {
1284+
urn.as_slice()
1285+
== "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha".as_bytes()
1286+
}
1287+
_ => false,
1288+
}
1289+
})
1290+
})
1291+
.next();
1292+
1293+
context.premultiplied_alpha = alpha_item_id.map_or(false, |alpha_item_id| {
1294+
meta.item_references.iter().any(|iref| {
1295+
iref.from_item_id == meta.primary_item_id
1296+
&& iref.to_item_id == alpha_item_id
1297+
&& iref.item_type == b"prem"
1298+
})
1299+
});
1300+
12611301
// load data of relevant items
12621302
for loc in meta.iloc_items.iter() {
1263-
if loc.item_id != meta.primary_item_id {
1303+
let mut item_data = if loc.item_id == meta.primary_item_id {
1304+
&mut context.primary_item
1305+
} else if Some(loc.item_id) == alpha_item_id {
1306+
context.alpha_item.get_or_insert_with(TryVec::new)
1307+
} else {
12641308
continue;
1265-
}
1309+
};
12661310

12671311
if loc.construction_method != ConstructionMethod::File {
12681312
return Err(Error::Unsupported("unsupported construction_method"));
12691313
}
1270-
let mut item_data = TryVec::new();
12711314
for extent in loc.extents.iter() {
12721315
let mut found = false;
12731316
// try to find an overlapping mdat
@@ -1283,13 +1326,11 @@ pub fn read_avif<T: Read>(f: &mut T, context: &mut AvifContext) -> Result<()> {
12831326
}
12841327
}
12851328
if !found {
1286-
return Err(Error::InvalidData("iloc contains an extent that is not in mdat"));
1329+
return Err(Error::InvalidData(
1330+
"iloc contains an extent that is not in mdat",
1331+
));
12871332
}
12881333
}
1289-
1290-
if loc.item_id == meta.primary_item_id {
1291-
context.primary_item = item_data;
1292-
}
12931334
}
12941335

12951336
Ok(())
@@ -1369,6 +1410,8 @@ fn read_avif_meta<T: Read + Offset>(src: &mut BMFFBox<T>) -> Result<AvifMeta> {
13691410
}
13701411

13711412
Ok(AvifMeta {
1413+
properties,
1414+
item_references,
13721415
primary_item_id,
13731416
iloc_items: iloc_items.ok_or(Error::InvalidData("iloc missing"))?,
13741417
})
@@ -1541,8 +1584,8 @@ pub enum ImageProperty {
15411584
impl ImageProperty {
15421585
fn clone(&self) -> Result<Self> {
15431586
Ok(match self {
1544-
Self::Channels(val) => Self::Channels(val.clone()?),
1545-
Self::AuxiliaryType(val) => Self::AuxiliaryType(val.clone()?),
1587+
Self::Channels(val) => Self::Channels(val.try_clone()?),
1588+
Self::AuxiliaryType(val) => Self::AuxiliaryType(val.try_clone()?),
15461589
Self::Unsupported => Self::Unsupported,
15471590
})
15481591
}

mp4parse/tests/kodim-extents.avif

4.48 KB
Binary file not shown.

mp4parse/tests/public.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ static AUDIO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_audio_init.mp4";
2828
static VIDEO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_video_init.mp4";
2929
static VIDEO_AV1_MP4: &str = "tests/tiny_av1.mp4";
3030
static IMAGE_AVIF: &str = "av1-avif/testFiles/Microsoft/Monochrome.avif";
31+
static IMAGE_AVIF_EXTENTS: &str = "tests/kodim-extents.avif";
3132
static IMAGE_AVIF_CORRUPT: &str = "tests/bug-1655846.avif";
3233
static IMAGE_AVIF_CORRUPT_2: &str = "tests/bug-1661347.avif";
3334
static IMAGE_AVIF_GRID: &str = "av1-avif/testFiles/Microsoft/Summer_in_Tomsk_720p_5x4_grid.avif";
@@ -629,6 +630,14 @@ fn public_avif_primary_item() {
629630
assert_eq!(context.primary_item[0..4], [0x12, 0x00, 0x0a, 0x0a]);
630631
}
631632

633+
#[test]
634+
fn public_avif_primary_item_split_extents() {
635+
let context = &mut mp4::AvifContext::new();
636+
let input = &mut File::open(IMAGE_AVIF_EXTENTS).expect("Unknown file");
637+
mp4::read_avif(input, context).expect("read_avif failed");
638+
assert_eq!(context.primary_item.len(), 4387);
639+
}
640+
632641
#[test]
633642
fn public_avif_bug_1655846() {
634643
let context = &mut mp4::AvifContext::new();

0 commit comments

Comments
 (0)