Skip to content

Commit c6ba5af

Browse files
authored
Merge pull request #333 from mozilla/pixi-capi
Expose the pixel information (pixi) via the C API
2 parents 6aa2481 + 3cb73c9 commit c6ba5af

File tree

3 files changed

+65
-24
lines changed

3 files changed

+65
-24
lines changed

mp4parse/src/lib.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -932,14 +932,35 @@ pub struct AvifContext {
932932
}
933933

934934
impl AvifContext {
935-
pub fn primary_item(&self) -> &[u8] {
935+
pub fn primary_item_coded_data(&self) -> &[u8] {
936936
self.item_as_slice(&self.primary_item)
937937
}
938938

939-
pub fn alpha_item(&self) -> Option<&[u8]> {
939+
pub fn primary_item_bits_per_channel(&self) -> Result<&[u8]> {
940+
self.image_bits_per_channel(self.primary_item.id)
941+
}
942+
943+
pub fn alpha_item_coded_data(&self) -> &[u8] {
944+
self.alpha_item
945+
.as_ref()
946+
.map_or(&[], |item| self.item_as_slice(item))
947+
}
948+
949+
pub fn alpha_item_bits_per_channel(&self) -> Result<&[u8]> {
940950
self.alpha_item
941951
.as_ref()
942-
.map(|item| self.item_as_slice(item))
952+
.map_or(Ok(&[]), |item| self.image_bits_per_channel(item.id))
953+
}
954+
955+
fn image_bits_per_channel(&self, item_id: ItemId) -> Result<&[u8]> {
956+
match self
957+
.item_properties
958+
.get(item_id, BoxType::PixelInformationBox)?
959+
{
960+
Some(ItemProperty::Channels(pixi)) => Ok(pixi.bits_per_channel.as_slice()),
961+
Some(other_property) => panic!("property key mismatch: {:?}", other_property),
962+
None => Ok(&[]),
963+
}
943964
}
944965

945966
pub fn spatial_extents_ptr(&self) -> Result<*const ImageSpatialExtentsProperty> {
@@ -2258,7 +2279,7 @@ fn read_iprp<T: Read>(
22582279
pub enum ItemProperty {
22592280
AuxiliaryType(AuxiliaryTypeProperty),
22602281
AV1Config(AV1ConfigBox),
2261-
Channels(TryVec<u8>),
2282+
Channels(PixelInformation),
22622283
Colour(ColourInformation),
22632284
ImageSpatialExtents(ImageSpatialExtentsProperty),
22642285
Mirroring(ImageMirror),
@@ -2716,24 +2737,29 @@ fn read_ispe<T: Read>(src: &mut BMFFBox<T>) -> Result<ImageSpatialExtentsPropert
27162737
})
27172738
}
27182739

2740+
#[derive(Debug)]
2741+
pub struct PixelInformation {
2742+
bits_per_channel: TryVec<u8>,
2743+
}
2744+
27192745
/// Parse pixel information
27202746
/// See HEIF (ISO 23008-12:2017) § 6.5.6
2721-
fn read_pixi<T: Read>(src: &mut BMFFBox<T>) -> Result<TryVec<u8>> {
2747+
fn read_pixi<T: Read>(src: &mut BMFFBox<T>) -> Result<PixelInformation> {
27222748
let version = read_fullbox_version_no_flags(src)?;
27232749
if version != 0 {
27242750
return Err(Error::Unsupported("pixi version"));
27252751
}
27262752

2727-
let num_channels = src.read_u8()?.into();
2728-
let mut channels = TryVec::with_capacity(num_channels)?;
2729-
let num_channels_read = src.try_read_to_end(&mut channels)?;
2753+
let num_channels = src.read_u8()?;
2754+
let mut bits_per_channel = TryVec::with_capacity(num_channels.to_usize())?;
2755+
let num_channels_read = src.try_read_to_end(&mut bits_per_channel)?;
27302756

2731-
if num_channels_read != num_channels {
2757+
if u8::try_from(num_channels_read)? != num_channels {
27322758
return Err(Error::InvalidData("invalid num_channels"));
27332759
}
27342760

27352761
check_parser_state!(src.content);
2736-
Ok(channels)
2762+
Ok(PixelInformation { bits_per_channel })
27372763
}
27382764

27392765
/// Despite [Rec. ITU-T H.273] (12/2016) defining the CICP fields as having a

mp4parse/tests/public.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ fn public_avif_primary_item() {
758758
let input = &mut File::open(IMAGE_AVIF).expect("Unknown file");
759759
let context = mp4::read_avif(input, ParseStrictness::Normal).expect("read_avif failed");
760760
assert_eq!(
761-
context.primary_item(),
761+
context.primary_item_coded_data(),
762762
[
763763
0x12, 0x00, 0x0A, 0x07, 0x38, 0x00, 0x06, 0x90, 0x20, 0x20, 0x69, 0x32, 0x0C, 0x16,
764764
0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x79, 0x4C, 0xD2, 0x02
@@ -770,7 +770,7 @@ fn public_avif_primary_item() {
770770
fn public_avif_primary_item_split_extents() {
771771
let input = &mut File::open(IMAGE_AVIF_EXTENTS).expect("Unknown file");
772772
let context = mp4::read_avif(input, ParseStrictness::Normal).expect("read_avif failed");
773-
assert_eq!(context.primary_item().len(), 52);
773+
assert_eq!(context.primary_item_coded_data().len(), 52);
774774
}
775775

776776
#[test]
@@ -783,15 +783,15 @@ fn public_avif_alpha_item() {
783783
fn public_avif_alpha_non_premultiplied() {
784784
let input = &mut File::open(IMAGE_AVIF_ALPHA).expect("Unknown file");
785785
let context = mp4::read_avif(input, ParseStrictness::Normal).expect("read_avif failed");
786-
assert!(context.alpha_item().is_some());
786+
assert!(!context.alpha_item_coded_data().is_empty());
787787
assert!(!context.premultiplied_alpha);
788788
}
789789

790790
#[test]
791791
fn public_avif_alpha_premultiplied() {
792792
let input = &mut File::open(IMAGE_AVIF_ALPHA_PREMULTIPLIED).expect("Unknown file");
793793
let context = mp4::read_avif(input, ParseStrictness::Normal).expect("read_avif failed");
794-
assert!(context.alpha_item().is_some());
794+
assert!(!context.alpha_item_coded_data().is_empty());
795795
assert!(context.premultiplied_alpha);
796796
assert_avif_valid(input);
797797
}

mp4parse_capi/src/lib.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,25 @@ pub struct Mp4parseParser {
314314
video_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackVideoSampleInfo>>,
315315
}
316316

317+
#[repr(C)]
318+
#[derive(Debug)]
319+
pub struct Mp4parseAvifImageItem {
320+
pub coded_data: Mp4parseByteData,
321+
pub bits_per_channel: Mp4parseByteData,
322+
}
323+
317324
#[repr(C)]
318325
#[derive(Debug)]
319326
pub struct Mp4parseAvifImage {
320-
pub primary_item: Mp4parseByteData,
327+
pub primary_image: Mp4parseAvifImageItem,
321328
/// The size of the image; should never be null unless using permissive parsing
322329
pub spatial_extents: *const mp4parse::ImageSpatialExtentsProperty,
323330
pub nclx_colour_information: *const mp4parse::NclxColourInformation,
324331
pub icc_colour_information: Mp4parseByteData,
325332
pub image_rotation: mp4parse::ImageRotation,
326333
pub image_mirror: *const mp4parse::ImageMirror,
327-
/// If no alpha item exists, `.length` will be 0 and `.data` will be null
328-
pub alpha_item: Mp4parseByteData,
334+
/// If no alpha item exists, members' `.length` will be 0 and `.data` will be null
335+
pub alpha_image: Mp4parseAvifImageItem,
329336
pub premultiplied_alpha: bool,
330337
}
331338

@@ -1022,8 +1029,8 @@ fn mp4parse_get_track_video_info_safe(
10221029
/// pointer points to a valid `Mp4parseAvifParser`, and that the avif_image
10231030
/// pointer points to a valid `Mp4parseAvifImage`. If there was not a previous
10241031
/// successful call to `mp4parse_avif_read()`, no guarantees are made as to
1025-
/// the state of `avif_image`. If `avif_image.alpha_item` is set to a
1026-
/// positive `length` and non-null `data`, then the `avif_image` contains an
1032+
/// the state of `avif_image`. If `avif_image.alpha_image.coded_data` is set to
1033+
/// a positive `length` and non-null `data`, then the `avif_image` contains a
10271034
/// valid alpha channel data. Otherwise, the image is opaque.
10281035
#[no_mangle]
10291036
pub unsafe extern "C" fn mp4parse_avif_get_image(
@@ -1047,17 +1054,25 @@ pub fn mp4parse_avif_get_image_safe(
10471054
) -> mp4parse::Result<Mp4parseAvifImage> {
10481055
let context = parser.context();
10491056

1057+
let primary_image = Mp4parseAvifImageItem {
1058+
coded_data: Mp4parseByteData::with_data(context.primary_item_coded_data()),
1059+
bits_per_channel: Mp4parseByteData::with_data(context.primary_item_bits_per_channel()?),
1060+
};
1061+
1062+
// If there is no alpha present, all the `Mp4parseByteData`s will be zero length
1063+
let alpha_image = Mp4parseAvifImageItem {
1064+
coded_data: Mp4parseByteData::with_data(context.alpha_item_coded_data()),
1065+
bits_per_channel: Mp4parseByteData::with_data(context.alpha_item_bits_per_channel()?),
1066+
};
1067+
10501068
Ok(Mp4parseAvifImage {
1051-
primary_item: Mp4parseByteData::with_data(context.primary_item()),
1069+
primary_image,
10521070
spatial_extents: context.spatial_extents_ptr()?,
10531071
nclx_colour_information: context.nclx_colour_information_ptr()?,
10541072
icc_colour_information: Mp4parseByteData::with_data(context.icc_colour_information()?),
10551073
image_rotation: context.image_rotation()?,
10561074
image_mirror: context.image_mirror_ptr()?,
1057-
alpha_item: context
1058-
.alpha_item()
1059-
.map(Mp4parseByteData::with_data)
1060-
.unwrap_or_default(),
1075+
alpha_image,
10611076
premultiplied_alpha: context.premultiplied_alpha,
10621077
})
10631078
}

0 commit comments

Comments
 (0)