@@ -1120,6 +1120,205 @@ fn read_esds_mpeg2_aac_lc() {
11201120 assert_eq ! ( es. decoder_specific_data, aac_dc_descriptor) ;
11211121}
11221122
1123+ #[ test]
1124+ fn read_esds_truncated_dsi ( ) {
1125+ // HE-AAC DSI truncated so the GASpecificConfig tail (frameLengthFlag /
1126+ // dependsOnCoreCoder / extensionFlag) runs off the end of the 24-bit
1127+ // payload, causing the bit reader to hit EOF. Reduced from band-orion.de
1128+ // (Firefox bug 2011644).
1129+ //
1130+ // DSI `2b 92 08` decodes as:
1131+ // AOT=5 (SBR, explicit signalling)
1132+ // samplingFrequencyIndex=7 (22050 Hz base)
1133+ // channelConfiguration=2 (stereo)
1134+ // extensionSamplingFrequencyIndex=4 (44100 Hz extended)
1135+ // inner AOT=2 (AAC-LC)
1136+ // which leaves 2 bits remaining, one short of the extensionFlag.
1137+ //
1138+ // Before our fix read_ds_descriptor stored the parsed fields only after
1139+ // the failing reads, so find_descriptor would swallow the BitReaderError
1140+ // in non-strict mode and leave the ES_Descriptor with audio_sample_rate,
1141+ // audio_channel_count and decoder_specific_data all unset — a "playable"
1142+ // AAC track with no config, producing silent audio.
1143+ let aac_esds = vec ! [
1144+ 0x03 , 0x1a , // ES_Descriptor tag, len=26
1145+ 0x00 , 0x00 , 0x00 , // ES_ID + flags
1146+ 0x04 , 0x12 , // DC_Descriptor tag, len=18
1147+ 0x40 , 0x15 , 0x00 , 0x00 , 0x00 , // objType=AAC, streamType, bufferSize
1148+ 0x00 , 0x00 , 0x00 , 0x00 , // maxBitrate
1149+ 0x00 , 0x00 , 0x00 , 0x00 , // avgBitrate
1150+ 0x05 , 0x03 , 0x2b , 0x92 , 0x08 , // DSI: truncated HE-AAC
1151+ 0x06 , 0x01 , 0x02 , // SL_Descriptor
1152+ ] ;
1153+ let aac_dc_descriptor = & aac_esds[ 22 ..25 ] ;
1154+
1155+ let mut stream = make_box ( BoxSize :: Auto , b"esds" , |s| {
1156+ s. B32 ( 0 ) // reserved
1157+ . append_bytes ( aac_esds. as_slice ( ) )
1158+ } ) ;
1159+ let mut iter = super :: BoxIter :: new ( & mut stream) ;
1160+ let mut stream = iter. next_box ( ) . unwrap ( ) . unwrap ( ) ;
1161+
1162+ let es = super :: read_esds ( & mut stream, ParseStrictness :: Normal ) . unwrap ( ) ;
1163+
1164+ assert_eq ! ( es. audio_codec, super :: CodecType :: AAC ) ;
1165+ assert_eq ! ( es. audio_object_type, Some ( 2 ) ) ;
1166+ assert_eq ! ( es. extended_audio_object_type, Some ( 5 ) ) ;
1167+ assert_eq ! ( es. audio_sample_rate, Some ( 22050 ) ) ;
1168+ assert_eq ! ( es. audio_channel_count, Some ( 2 ) ) ;
1169+ assert_eq ! ( es. codec_esds, aac_esds) ;
1170+ assert_eq ! ( es. decoder_specific_data, aac_dc_descriptor) ;
1171+ }
1172+
1173+ #[ test]
1174+ fn read_esds_duplicate_dsi ( ) {
1175+ // Two DecSpecificInfo descriptors with identical AAC-LC 48 kHz stereo
1176+ // payload (`11 90`). Reduced from Firefox bug 1906838.
1177+ //
1178+ // In non-strict mode the parser must keep the first DSI and ignore the
1179+ // second — matching what Chromium (media/formats/mp4/es_descriptor.cc)
1180+ // and ffmpeg (libavformat/isom.c ff_mp4_read_dec_config_descr) do.
1181+ // Before our fix the parser concatenated both DSI payloads and
1182+ // overwrote audio_object_type / audio_sample_rate / audio_channel_count
1183+ // with the second DSI's parsed values, which left the ES_Descriptor in
1184+ // an inconsistent state when the two DSIs disagreed.
1185+ let aac_esds = vec ! [
1186+ 0x03 , 0x1d , // ES_Descriptor tag, len=29
1187+ 0x00 , 0x00 , 0x00 , // ES_ID + flags
1188+ 0x04 , 0x15 , // DC_Descriptor tag, len=21
1189+ 0x40 , 0x15 , 0x00 , 0x00 , 0x00 , // objType=AAC, streamType, bufferSize
1190+ 0x00 , 0x00 , 0x01 , 0xf4 , // maxBitrate
1191+ 0x00 , 0x00 , 0x01 , 0xf4 , // avgBitrate
1192+ 0x05 , 0x02 , 0x11 , 0x90 , // DSI #1 (kept)
1193+ 0x05 , 0x02 , 0x11 , 0x90 , // DSI #2 (ignored in non-strict)
1194+ 0x06 , 0x01 , 0x02 , // SL_Descriptor
1195+ ] ;
1196+ let aac_dc_descriptor = & aac_esds[ 22 ..24 ] ;
1197+
1198+ let mut stream = make_box ( BoxSize :: Auto , b"esds" , |s| {
1199+ s. B32 ( 0 ) // reserved
1200+ . append_bytes ( aac_esds. as_slice ( ) )
1201+ } ) ;
1202+ let mut iter = super :: BoxIter :: new ( & mut stream) ;
1203+ let mut stream = iter. next_box ( ) . unwrap ( ) . unwrap ( ) ;
1204+
1205+ let es = super :: read_esds ( & mut stream, ParseStrictness :: Normal ) . unwrap ( ) ;
1206+
1207+ assert_eq ! ( es. audio_codec, super :: CodecType :: AAC ) ;
1208+ assert_eq ! ( es. audio_object_type, Some ( 2 ) ) ;
1209+ assert_eq ! ( es. extended_audio_object_type, None ) ;
1210+ assert_eq ! ( es. audio_sample_rate, Some ( 48000 ) ) ;
1211+ assert_eq ! ( es. audio_channel_count, Some ( 2 ) ) ;
1212+ assert_eq ! ( es. codec_esds, aac_esds) ;
1213+ // First DSI's bytes only — not the concatenation of both.
1214+ assert_eq ! ( es. decoder_specific_data, aac_dc_descriptor) ;
1215+ }
1216+
1217+ #[ test]
1218+ fn read_esds_duplicate_dsi_strict ( ) {
1219+ // In strict mode, the duplicate-DSI case is rejected with the
1220+ // corresponding status rather than silently kept as "first wins".
1221+ let aac_esds = vec ! [
1222+ 0x03 , 0x1d , 0x00 , 0x00 , 0x00 , 0x04 , 0x15 , 0x40 , 0x15 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 ,
1223+ 0xf4 , 0x00 , 0x00 , 0x01 , 0xf4 , 0x05 , 0x02 , 0x11 , 0x90 , 0x05 , 0x02 , 0x11 , 0x90 , 0x06 , 0x01 ,
1224+ 0x02 ,
1225+ ] ;
1226+
1227+ let mut stream = make_box ( BoxSize :: Auto , b"esds" , |s| {
1228+ s. B32 ( 0 ) // reserved
1229+ . append_bytes ( aac_esds. as_slice ( ) )
1230+ } ) ;
1231+ let mut iter = super :: BoxIter :: new ( & mut stream) ;
1232+ let mut stream = iter. next_box ( ) . unwrap ( ) . unwrap ( ) ;
1233+
1234+ match super :: read_esds ( & mut stream, ParseStrictness :: Strict ) {
1235+ Err ( Error :: InvalidData ( Status :: EsdsDecSpecificInfoTagQuantity ) ) => { }
1236+ other => panic ! (
1237+ "expected EsdsDecSpecificInfoTagQuantity in strict mode, got {:?}" ,
1238+ other
1239+ ) ,
1240+ }
1241+ }
1242+
1243+ #[ test]
1244+ fn read_esds_duplicate_dsi_disagreeing ( ) {
1245+ // Two DecSpecificInfo descriptors whose payloads disagree on sample rate
1246+ // and channel count. Locks in "first-wins" rather than "second-wins" or
1247+ // "concatenate":
1248+ // DSI #1 `11 90` → AOT=2 (AAC-LC), SFI=3 (48 kHz), chCfg=2 (stereo)
1249+ // DSI #2 `12 88` → AOT=2 (AAC-LC), SFI=5 (32 kHz), chCfg=1 (mono)
1250+ let aac_esds = vec ! [
1251+ 0x03 , 0x1d , // ES_Descriptor tag, len=29
1252+ 0x00 , 0x00 , 0x00 , // ES_ID + flags
1253+ 0x04 , 0x15 , // DC_Descriptor tag, len=21
1254+ 0x40 , 0x15 , 0x00 , 0x00 , 0x00 , // objType=AAC, streamType, bufferSize
1255+ 0x00 , 0x00 , 0x01 , 0xf4 , // maxBitrate
1256+ 0x00 , 0x00 , 0x01 , 0xf4 , // avgBitrate
1257+ 0x05 , 0x02 , 0x11 , 0x90 , // DSI #1: 48 kHz stereo (kept)
1258+ 0x05 , 0x02 , 0x12 , 0x88 , // DSI #2: 32 kHz mono (ignored in non-strict)
1259+ 0x06 , 0x01 , 0x02 , // SL_Descriptor
1260+ ] ;
1261+ let dsi1 = & aac_esds[ 22 ..24 ] ;
1262+
1263+ let mut stream = make_box ( BoxSize :: Auto , b"esds" , |s| {
1264+ s. B32 ( 0 ) // reserved
1265+ . append_bytes ( aac_esds. as_slice ( ) )
1266+ } ) ;
1267+ let mut iter = super :: BoxIter :: new ( & mut stream) ;
1268+ let mut stream = iter. next_box ( ) . unwrap ( ) . unwrap ( ) ;
1269+
1270+ let es = super :: read_esds ( & mut stream, ParseStrictness :: Normal ) . unwrap ( ) ;
1271+
1272+ assert_eq ! ( es. audio_codec, super :: CodecType :: AAC ) ;
1273+ assert_eq ! ( es. audio_object_type, Some ( 2 ) ) ;
1274+ assert_eq ! ( es. extended_audio_object_type, None ) ;
1275+ // First DSI wins — not 32000 / 1 from DSI #2.
1276+ assert_eq ! ( es. audio_sample_rate, Some ( 48000 ) ) ;
1277+ assert_eq ! ( es. audio_channel_count, Some ( 2 ) ) ;
1278+ assert_eq ! ( es. decoder_specific_data, dsi1) ;
1279+ }
1280+
1281+ #[ test]
1282+ fn read_esds_truncated_dsi_pce ( ) {
1283+ // Truncated DSI with channel_configuration == 0 (program_config_element
1284+ // signalling). The PCE tail is needed to derive the channel count, so
1285+ // the fix's early-store cannot populate audio_channel_count or the DSI
1286+ // bytes — only AOT / extended_AOT / sample_rate.
1287+ //
1288+ // DSI `11 80` decodes as:
1289+ // AOT=2 (AAC-LC), SFI=3 (48 kHz), chCfg=0 (PCE)
1290+ // frameLengthFlag=1, dependsOnCoreCoder=0, extensionFlag=0
1291+ // after which the 4-bit element_instance_tag read runs off the end.
1292+ let aac_esds = vec ! [
1293+ 0x03 , 0x19 , // ES_Descriptor tag, len=25
1294+ 0x00 , 0x00 , 0x00 , // ES_ID + flags
1295+ 0x04 , 0x11 , // DC_Descriptor tag, len=17
1296+ 0x40 , 0x15 , 0x00 , 0x00 , 0x00 , // objType=AAC, streamType, bufferSize
1297+ 0x00 , 0x00 , 0x01 , 0xf4 , // maxBitrate
1298+ 0x00 , 0x00 , 0x01 , 0xf4 , // avgBitrate
1299+ 0x05 , 0x02 , 0x11 , 0x80 , // DSI: AAC-LC 48 kHz PCE, truncated mid-PCE
1300+ 0x06 , 0x01 , 0x02 , // SL_Descriptor
1301+ ] ;
1302+
1303+ let mut stream = make_box ( BoxSize :: Auto , b"esds" , |s| {
1304+ s. B32 ( 0 ) // reserved
1305+ . append_bytes ( aac_esds. as_slice ( ) )
1306+ } ) ;
1307+ let mut iter = super :: BoxIter :: new ( & mut stream) ;
1308+ let mut stream = iter. next_box ( ) . unwrap ( ) . unwrap ( ) ;
1309+
1310+ let es = super :: read_esds ( & mut stream, ParseStrictness :: Normal ) . unwrap ( ) ;
1311+
1312+ assert_eq ! ( es. audio_codec, super :: CodecType :: AAC ) ;
1313+ assert_eq ! ( es. audio_object_type, Some ( 2 ) ) ;
1314+ assert_eq ! ( es. extended_audio_object_type, None ) ;
1315+ assert_eq ! ( es. audio_sample_rate, Some ( 48000 ) ) ;
1316+ // PCE parsing hits EOF before deriving the channel count, so these stay
1317+ // unset rather than guessing.
1318+ assert_eq ! ( es. audio_channel_count, None ) ;
1319+ assert ! ( es. decoder_specific_data. is_empty( ) ) ;
1320+ }
1321+
11231322#[ test]
11241323fn read_stsd_mp4v ( ) {
11251324 let mp4v = vec ! [
0 commit comments