@@ -148,6 +148,87 @@ struct HashMap;
148
148
#[ allow( dead_code) ]
149
149
struct String ;
150
150
151
+ /// The return value to the C API
152
+ /// Any detail that needs to be communicated to the caller must be encoded here
153
+ /// since the [`Error`] type's associated data is part of the FFI.
154
+ #[ repr( C ) ]
155
+ #[ derive( PartialEq , Debug ) ]
156
+ pub enum Status {
157
+ Ok = 0 ,
158
+ BadArg = 1 ,
159
+ Invalid = 2 ,
160
+ Unsupported = 3 ,
161
+ Eof = 4 ,
162
+ Io = 5 ,
163
+ Oom = 6 ,
164
+ UnsupportedA1lx ,
165
+ UnsupportedA1op ,
166
+ UnsupportedClap ,
167
+ UnsupportedGrid ,
168
+ UnsupportedIpro ,
169
+ UnsupportedLsel ,
170
+ }
171
+
172
+ /// For convenience of creating an error for an unsupported feature which we
173
+ /// want to communicate the specific feature back to the C API caller
174
+ impl From < Status > for Error {
175
+ fn from ( parse_status : Status ) -> Self {
176
+ let msg = match parse_status {
177
+ Status :: Ok
178
+ | Status :: BadArg
179
+ | Status :: Invalid
180
+ | Status :: Unsupported
181
+ | Status :: Eof
182
+ | Status :: Io
183
+ | Status :: Oom => {
184
+ panic ! ( "Status -> Error is only for Status:UnsupportedXXX errors" )
185
+ }
186
+
187
+ Status :: UnsupportedA1lx => "AV1 layered image indexing (a1lx) is unsupported" ,
188
+ Status :: UnsupportedA1op => "Operating point selection (a1op) is unsupported" ,
189
+ Status :: UnsupportedClap => "Clean aperture (clap) transform is unsupported" ,
190
+ Status :: UnsupportedGrid => "Grid-based images are unsupported" ,
191
+ Status :: UnsupportedIpro => "Item protection (ipro) is unsupported" ,
192
+ Status :: UnsupportedLsel => "Layer selection (lsel) is unsupported" ,
193
+ } ;
194
+ Self :: UnsupportedDetail ( parse_status, msg)
195
+ }
196
+ }
197
+
198
+ impl From < Error > for Status {
199
+ fn from ( error : Error ) -> Self {
200
+ match error {
201
+ Error :: NoMoov | Error :: InvalidData ( _) => Self :: Invalid ,
202
+ Error :: Unsupported ( _) => Self :: Unsupported ,
203
+ Error :: UnsupportedDetail ( parse_status, _msg) => parse_status,
204
+ Error :: UnexpectedEOF => Self :: Eof ,
205
+ Error :: Io ( _) => {
206
+ // Getting std::io::ErrorKind::UnexpectedEof is normal
207
+ // but our From trait implementation should have converted
208
+ // those to our Error::UnexpectedEOF variant.
209
+ Self :: Io
210
+ }
211
+ Error :: OutOfMemory => Self :: Oom ,
212
+ }
213
+ }
214
+ }
215
+
216
+ impl From < Result < ( ) , Status > > for Status {
217
+ fn from ( result : Result < ( ) , Status > ) -> Self {
218
+ match result {
219
+ Ok ( ( ) ) => Status :: Ok ,
220
+ Err ( Status :: Ok ) => unreachable ! ( ) ,
221
+ Err ( e) => e,
222
+ }
223
+ }
224
+ }
225
+
226
+ impl From < fallible_collections:: TryReserveError > for Status {
227
+ fn from ( _: fallible_collections:: TryReserveError ) -> Self {
228
+ Status :: Oom
229
+ }
230
+ }
231
+
151
232
/// Describes parser failures.
152
233
///
153
234
/// This enum wraps the standard `io::Error` type, unified with
@@ -158,6 +239,10 @@ pub enum Error {
158
239
InvalidData ( & ' static str ) ,
159
240
/// Parse error caused by limited parser support rather than invalid data.
160
241
Unsupported ( & ' static str ) ,
242
+ /// Similar to [`Self::Unsupported`], but for errors that have a specific
243
+ /// [`Status`] variant for communicating the detail across FFI.
244
+ /// See the helper [`From<Status> for Error`](enum.Error.html#impl-From<Status>)
245
+ UnsupportedDetail ( Status , & ' static str ) ,
161
246
/// Reflect `std::io::ErrorKind::UnexpectedEof` for short data.
162
247
UnexpectedEOF ,
163
248
/// Propagate underlying errors from `std::io`.
@@ -1857,9 +1942,15 @@ fn read_avif_meta<T: Read + Offset>(
1857
1942
let item_infos = item_infos. ok_or ( Error :: InvalidData ( "iinf missing" ) ) ?;
1858
1943
1859
1944
if let Some ( item_info) = item_infos. iter ( ) . find ( |x| x. item_id == primary_item_id) {
1860
- if & item_info. item_type . to_be_bytes ( ) != b"av01" {
1861
- warn ! ( "primary_item_id type: {}" , U32BE ( item_info. item_type) ) ;
1862
- return Err ( Error :: InvalidData ( "primary_item_id type is not av01" ) ) ;
1945
+ debug ! ( "primary_item_id type: {}" , U32BE ( item_info. item_type) ) ;
1946
+ match & item_info. item_type . to_be_bytes ( ) {
1947
+ b"av01" => { }
1948
+ b"grid" => return Err ( Error :: from ( Status :: UnsupportedGrid ) ) ,
1949
+ _ => {
1950
+ return Err ( Error :: InvalidData (
1951
+ "primary_item_id type is neither 'av01' nor 'grid'" ,
1952
+ ) )
1953
+ }
1863
1954
}
1864
1955
} else {
1865
1956
return Err ( Error :: InvalidData (
@@ -1964,9 +2055,7 @@ fn read_infe<T: Read>(src: &mut BMFFBox<T>, strictness: ParseStrictness) -> Resu
1964
2055
let item_protection_index = be_u16 ( src) ?;
1965
2056
1966
2057
if item_protection_index != 0 {
1967
- return Err ( Error :: Unsupported (
1968
- "protected items (infe.item_protection_index != 0) are not supported" ,
1969
- ) ) ;
2058
+ return Err ( Error :: from ( Status :: UnsupportedIpro ) ) ;
1970
2059
}
1971
2060
1972
2061
let item_type = be_u32 ( src) ?;
@@ -2109,6 +2198,9 @@ fn read_iprp<T: Read>(
2109
2198
| ItemProperty :: Rotation ( _) => {
2110
2199
if !a. essential {
2111
2200
warn ! ( "{:?} is invalid" , property) ;
2201
+ // This is a "shall", but it is likely to change, so only
2202
+ // fail if using strict parsing.
2203
+ // See https://github.com/mozilla/mp4parse-rust/issues/284
2112
2204
fail_if (
2113
2205
strictness == ParseStrictness :: Strict ,
2114
2206
"All transformative properties associated with coded and \
@@ -2532,6 +2624,11 @@ fn read_ipma<T: Read>(
2532
2624
2533
2625
/// Parse an ItemPropertyContainerBox
2534
2626
///
2627
+ /// For unsupported properties that we know about, return specific
2628
+ /// [`Status`] UnsupportedXXXX variants. Unless running in
2629
+ /// [`ParseStrictness::Permissive`] mode, in which case, unsupported properties
2630
+ /// will be ignored.
2631
+ ///
2535
2632
/// See ISOBMFF (ISO 14496-12:2020 § 8.11.14.1
2536
2633
fn read_ipco < T : Read > (
2537
2634
src : & mut BMFFBox < T > ,
@@ -2545,6 +2642,14 @@ fn read_ipco<T: Read>(
2545
2642
if let Some ( property) = match b. head . name {
2546
2643
BoxType :: AuxiliaryTypeProperty => Some ( ItemProperty :: AuxiliaryType ( read_auxc ( & mut b) ?) ) ,
2547
2644
BoxType :: AV1CodecConfigurationBox => Some ( ItemProperty :: AV1Config ( read_av1c ( & mut b) ?) ) ,
2645
+ BoxType :: AV1LayeredImageIndexingProperty
2646
+ if strictness != ParseStrictness :: Permissive =>
2647
+ {
2648
+ return Err ( Error :: from ( Status :: UnsupportedA1lx ) )
2649
+ }
2650
+ BoxType :: CleanApertureBox if strictness != ParseStrictness :: Permissive => {
2651
+ return Err ( Error :: from ( Status :: UnsupportedClap ) )
2652
+ }
2548
2653
BoxType :: ColourInformationBox => {
2549
2654
Some ( ItemProperty :: Colour ( read_colr ( & mut b, strictness) ?) )
2550
2655
}
@@ -2553,6 +2658,14 @@ fn read_ipco<T: Read>(
2553
2658
BoxType :: ImageSpatialExtentsProperty => {
2554
2659
Some ( ItemProperty :: ImageSpatialExtents ( read_ispe ( & mut b) ?) )
2555
2660
}
2661
+ BoxType :: LayerSelectorProperty if strictness != ParseStrictness :: Permissive => {
2662
+ return Err ( Error :: from ( Status :: UnsupportedLsel ) )
2663
+ }
2664
+ BoxType :: OperatingPointSelectorProperty
2665
+ if strictness != ParseStrictness :: Permissive =>
2666
+ {
2667
+ return Err ( Error :: from ( Status :: UnsupportedA1op ) )
2668
+ }
2556
2669
BoxType :: PixelInformationBox => Some ( ItemProperty :: Channels ( read_pixi ( & mut b) ?) ) ,
2557
2670
other_box_type => {
2558
2671
// Though we don't do anything with other property types, we still store
0 commit comments