@@ -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`.
@@ -1850,9 +1935,15 @@ fn read_avif_meta<T: Read + Offset>(
1850
1935
let item_infos = item_infos. ok_or ( Error :: InvalidData ( "iinf missing" ) ) ?;
1851
1936
1852
1937
if let Some ( item_info) = item_infos. iter ( ) . find ( |x| x. item_id == primary_item_id) {
1853
- if & item_info. item_type . to_be_bytes ( ) != b"av01" {
1854
- warn ! ( "primary_item_id type: {}" , U32BE ( item_info. item_type) ) ;
1855
- return Err ( Error :: InvalidData ( "primary_item_id type is not av01" ) ) ;
1938
+ debug ! ( "primary_item_id type: {}" , U32BE ( item_info. item_type) ) ;
1939
+ match & item_info. item_type . to_be_bytes ( ) {
1940
+ b"av01" => { }
1941
+ b"grid" => return Err ( Error :: from ( Status :: UnsupportedGrid ) ) ,
1942
+ _ => {
1943
+ return Err ( Error :: InvalidData (
1944
+ "primary_item_id type is neither 'av01' nor 'grid'" ,
1945
+ ) )
1946
+ }
1856
1947
}
1857
1948
} else {
1858
1949
return Err ( Error :: InvalidData (
@@ -1957,9 +2048,7 @@ fn read_infe<T: Read>(src: &mut BMFFBox<T>, strictness: ParseStrictness) -> Resu
1957
2048
let item_protection_index = be_u16 ( src) ?;
1958
2049
1959
2050
if item_protection_index != 0 {
1960
- return Err ( Error :: Unsupported (
1961
- "protected items (infe.item_protection_index != 0) are not supported" ,
1962
- ) ) ;
2051
+ return Err ( Error :: from ( Status :: UnsupportedIpro ) ) ;
1963
2052
}
1964
2053
1965
2054
let item_type = be_u32 ( src) ?;
@@ -2102,6 +2191,9 @@ fn read_iprp<T: Read>(
2102
2191
| ItemProperty :: Rotation ( _) => {
2103
2192
if !a. essential {
2104
2193
warn ! ( "{:?} is invalid" , property) ;
2194
+ // This is a "shall", but it is likely to change, so only
2195
+ // fail if using strict parsing.
2196
+ // See https://github.com/mozilla/mp4parse-rust/issues/284
2105
2197
fail_if (
2106
2198
strictness == ParseStrictness :: Strict ,
2107
2199
"All transformative properties associated with coded and \
@@ -2525,6 +2617,11 @@ fn read_ipma<T: Read>(
2525
2617
2526
2618
/// Parse an ItemPropertyContainerBox
2527
2619
///
2620
+ /// For unsupported properties that we know about, return specific
2621
+ /// [`Status`] UnsupportedXXXX variants. Unless running in
2622
+ /// [`ParseStrictness::Permissive`] mode, in which case, unsupported properties
2623
+ /// will be ignored.
2624
+ ///
2528
2625
/// See ISOBMFF (ISO 14496-12:2020 § 8.11.14.1
2529
2626
fn read_ipco < T : Read > (
2530
2627
src : & mut BMFFBox < T > ,
@@ -2538,6 +2635,14 @@ fn read_ipco<T: Read>(
2538
2635
if let Some ( property) = match b. head . name {
2539
2636
BoxType :: AuxiliaryTypeProperty => Some ( ItemProperty :: AuxiliaryType ( read_auxc ( & mut b) ?) ) ,
2540
2637
BoxType :: AV1CodecConfigurationBox => Some ( ItemProperty :: AV1Config ( read_av1c ( & mut b) ?) ) ,
2638
+ BoxType :: AV1LayeredImageIndexingProperty
2639
+ if strictness != ParseStrictness :: Permissive =>
2640
+ {
2641
+ return Err ( Error :: from ( Status :: UnsupportedA1lx ) )
2642
+ }
2643
+ BoxType :: CleanApertureBox if strictness != ParseStrictness :: Permissive => {
2644
+ return Err ( Error :: from ( Status :: UnsupportedClap ) )
2645
+ }
2541
2646
BoxType :: ColourInformationBox => {
2542
2647
Some ( ItemProperty :: Colour ( read_colr ( & mut b, strictness) ?) )
2543
2648
}
@@ -2546,6 +2651,14 @@ fn read_ipco<T: Read>(
2546
2651
BoxType :: ImageSpatialExtentsProperty => {
2547
2652
Some ( ItemProperty :: ImageSpatialExtents ( read_ispe ( & mut b) ?) )
2548
2653
}
2654
+ BoxType :: LayerSelectorProperty if strictness != ParseStrictness :: Permissive => {
2655
+ return Err ( Error :: from ( Status :: UnsupportedLsel ) )
2656
+ }
2657
+ BoxType :: OperatingPointSelectorProperty
2658
+ if strictness != ParseStrictness :: Permissive =>
2659
+ {
2660
+ return Err ( Error :: from ( Status :: UnsupportedA1op ) )
2661
+ }
2549
2662
BoxType :: PixelInformationBox => Some ( ItemProperty :: Channels ( read_pixi ( & mut b) ?) ) ,
2550
2663
other_box_type => {
2551
2664
// Though we don't do anything with other property types, we still store
0 commit comments