Commit b6b28267 by ibaker Committed by Marc Baechinger

Permit duplicate Opus headers

This reinstates the permissive behaviour removed by
https://github.com/google/ExoPlayer/commit/fe7e5b8181333683fda7869c3d9cffc0bdefcccb

Test file created by opening bear.opus in a hex editor and naively
duplicating the two header packets, starting at (and including) the
first `OggS` in the file and ending just before the third `OggS`.

#minor-release

Issue: google/ExoPlayer#10038
PiperOrigin-RevId: 452015662
parent 5460ac8e
......@@ -15,7 +15,6 @@
*/
package com.google.android.exoplayer2.extractor.ogg;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import androidx.annotation.Nullable;
......@@ -39,11 +38,21 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
'O', 'p', 'u', 's', 'T', 'a', 'g', 's'
};
private boolean firstCommentHeaderSeen;
public static boolean verifyBitstreamType(ParsableByteArray data) {
return peekPacketStartsWith(data, OPUS_ID_HEADER_SIGNATURE);
}
@Override
protected void reset(boolean headerData) {
super.reset(headerData);
if (headerData) {
firstCommentHeaderSeen = false;
}
}
@Override
protected long preparePayload(ParsableByteArray packet) {
return convertTimeToGranule(getPacketDurationUs(packet.getData()));
}
......@@ -57,9 +66,15 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
int channelCount = OpusUtil.getChannelCount(headerBytes);
List<byte[]> initializationData = OpusUtil.buildInitializationData(headerBytes);
// The ID header must come at the start of the file:
// https://datatracker.ietf.org/doc/html/rfc7845#section-3
checkState(setupData.format == null);
if (setupData.format != null) {
// setupData.format being non-null indicates we've already seen an ID header. Multiple ID
// headers are not permitted by the Opus spec [1], but have been observed in real files [2],
// so we just ignore all subsequent ones.
// [1] https://datatracker.ietf.org/doc/html/rfc7845#section-3 and
// https://datatracker.ietf.org/doc/html/rfc7845#section-5
// [2] https://github.com/google/ExoPlayer/issues/10038
return true;
}
setupData.format =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_OPUS)
......@@ -72,6 +87,15 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
// The comment header must come immediately after the ID header, so the format will already
// be populated: https://datatracker.ietf.org/doc/html/rfc7845#section-3
checkStateNotNull(setupData.format);
if (firstCommentHeaderSeen) {
// Multiple comment headers are not permitted by the Opus spec [1], but have been observed
// in real files [2], so we just ignore all subsequent ones.
// [1] https://datatracker.ietf.org/doc/html/rfc7845#section-3 and
// https://datatracker.ietf.org/doc/html/rfc7845#section-5
// [2] https://github.com/google/ExoPlayer/issues/10038
return true;
}
firstCommentHeaderSeen = true;
packet.skipBytes(OPUS_COMMENT_HEADER_SIGNATURE.length);
VorbisUtil.CommentHeader commentHeader =
VorbisUtil.readVorbisCommentHeader(
......
......@@ -43,6 +43,13 @@ public final class OggExtractorParameterizedTest {
ExtractorAsserts.assertBehavior(OggExtractor::new, "media/ogg/bear.opus", simulationConfig);
}
// https://github.com/google/ExoPlayer/issues/10038
@Test
public void opus_duplicateHeader() throws Exception {
ExtractorAsserts.assertBehavior(
OggExtractor::new, "media/ogg/bear_duplicate_header.opus", simulationConfig);
}
@Test
public void flac() throws Exception {
ExtractorAsserts.assertBehavior(OggExtractor::new, "media/ogg/bear_flac.ogg", simulationConfig);
......
seekMap:
isSeekable = true
duration = 2747500
getPosition(0) = [[timeUs=0, position=250]]
getPosition(1) = [[timeUs=1, position=250]]
getPosition(1373750) = [[timeUs=1373750, position=250]]
getPosition(2747500) = [[timeUs=2747500, position=250]]
numberOfTracks = 1
track 0:
total output bytes = 8698
sample count = 92
format 0:
sampleMimeType = audio/opus
channelCount = 2
sampleRate = 48000
metadata = entries=[VC: encoder=Lavf54.20.4]
initializationData:
data = length 19, hash BFE794DB
data = length 8, hash CA22068C
data = length 8, hash 79C07075
sample 0:
time = 1830000
flags = 1
data = length 86, hash 64D21AA1
sample 1:
time = 1840000
flags = 1
data = length 86, hash A1FAAF2C
sample 2:
time = 1850000
flags = 1
data = length 86, hash ECA80F7E
sample 3:
time = 1860000
flags = 1
data = length 86, hash FEB03B2C
sample 4:
time = 1870000
flags = 1
data = length 85, hash 2C2E6B2F
sample 5:
time = 1880000
flags = 1
data = length 89, hash A0D7AC3
sample 6:
time = 1890000
flags = 1
data = length 87, hash 83739547
sample 7:
time = 1900000
flags = 1
data = length 86, hash 991E531E
sample 8:
time = 1910000
flags = 1
data = length 88, hash 16B287A3
sample 9:
time = 1920000
flags = 1
data = length 86, hash FC86EED
sample 10:
time = 1930000
flags = 1
data = length 86, hash 96AF0248
sample 11:
time = 1940000
flags = 1
data = length 86, hash 288402C8
sample 12:
time = 1950000
flags = 1
data = length 87, hash 4BBA7912
sample 13:
time = 1960000
flags = 1
data = length 86, hash 4A59C719
sample 14:
time = 1970000
flags = 1
data = length 85, hash 906E8187
sample 15:
time = 1980000
flags = 1
data = length 90, hash CB8B755D
sample 16:
time = 1990000
flags = 1
data = length 87, hash C8E02C
sample 17:
time = 2000000
flags = 1
data = length 88, hash ACF4D89A
sample 18:
time = 2010000
flags = 1
data = length 86, hash 510FE048
sample 19:
time = 2020000
flags = 1
data = length 86, hash 64983E46
sample 20:
time = 2030000
flags = 1
data = length 86, hash CEA76A1E
sample 21:
time = 2040000
flags = 1
data = length 87, hash AADE498E
sample 22:
time = 2050000
flags = 1
data = length 127, hash 353A6D8C
sample 23:
time = 2060000
flags = 1
data = length 87, hash 29E18E62
sample 24:
time = 2070000
flags = 1
data = length 87, hash 2CF7B30F
sample 25:
time = 2080000
flags = 1
data = length 94, hash 758704C3
sample 26:
time = 2090000
flags = 1
data = length 88, hash C2153A4C
sample 27:
time = 2100000
flags = 1
data = length 86, hash A0A83DA5
sample 28:
time = 2110000
flags = 1
data = length 86, hash 41017D7F
sample 29:
time = 2120000
flags = 1
data = length 93, hash 686B0CA2
sample 30:
time = 2130000
flags = 1
data = length 86, hash 554D16CC
sample 31:
time = 2140000
flags = 1
data = length 88, hash 99D72771
sample 32:
time = 2150000
flags = 1
data = length 88, hash 7176DFBF
sample 33:
time = 2160000
flags = 1
data = length 86, hash BAA22669
sample 34:
time = 2170000
flags = 1
data = length 88, hash B00B0D3C
sample 35:
time = 2180000
flags = 1
data = length 89, hash 73FED83A
sample 36:
time = 2190000
flags = 1
data = length 86, hash 4A4138D3
sample 37:
time = 2200000
flags = 1
data = length 89, hash E0A860FF
sample 38:
time = 2210000
flags = 1
data = length 95, hash EE5A8AED
sample 39:
time = 2220000
flags = 1
data = length 92, hash 36DBD7FD
sample 40:
time = 2230000
flags = 1
data = length 88, hash EE47A7E4
sample 41:
time = 2240000
flags = 1
data = length 100, hash 2E1A603F
sample 42:
time = 2250000
flags = 1
data = length 89, hash 657ED4A3
sample 43:
time = 2260000
flags = 1
data = length 86, hash A833DC7B
sample 44:
time = 2270000
flags = 1
data = length 88, hash 81E80732
sample 45:
time = 2280000
flags = 1
data = length 91, hash FA256A0F
sample 46:
time = 2290000
flags = 1
data = length 88, hash A63A4DBA
sample 47:
time = 2300000
flags = 1
data = length 88, hash 67910A9F
sample 48:
time = 2310000
flags = 1
data = length 86, hash EB387DB6
sample 49:
time = 2320000
flags = 1
data = length 88, hash 5ACAAC2A
sample 50:
time = 2330000
flags = 1
data = length 86, hash 6ADF2E1F
sample 51:
time = 2340000
flags = 1
data = length 85, hash 9D064471
sample 52:
time = 2350000
flags = 1
data = length 87, hash F176C59
sample 53:
time = 2360000
flags = 1
data = length 89, hash 5CA40CE4
sample 54:
time = 2370000
flags = 1
data = length 88, hash 67B944FC
sample 55:
time = 2380000
flags = 1
data = length 86, hash B3A84EC8
sample 56:
time = 2390000
flags = 1
data = length 92, hash A6ACF94B
sample 57:
time = 2400000
flags = 1
data = length 88, hash CB0C9730
sample 58:
time = 2410000
flags = 1
data = length 88, hash C79FE804
sample 59:
time = 2420000
flags = 1
data = length 88, hash A74C7F0A
sample 60:
time = 2430000
flags = 1
data = length 91, hash 55F6F0A5
sample 61:
time = 2440000
flags = 1
data = length 93, hash 330F33E7
sample 62:
time = 2450000
flags = 1
data = length 89, hash 614AFBA0
sample 63:
time = 2460000
flags = 1
data = length 87, hash 3CE4652D
sample 64:
time = 2470000
flags = 1
data = length 87, hash 4EFD5467
sample 65:
time = 2480000
flags = 1
data = length 86, hash D81B3EB8
sample 66:
time = 2490000
flags = 1
data = length 88, hash 96CB6871
sample 67:
time = 2500000
flags = 1
data = length 88, hash E9DF2786
sample 68:
time = 2510000
flags = 1
data = length 89, hash 2CA33D96
sample 69:
time = 2520000
flags = 1
data = length 90, hash 96BDE594
sample 70:
time = 2530000
flags = 1
data = length 87, hash C261493C
sample 71:
time = 2540000
flags = 1
data = length 86, hash D037318E
sample 72:
time = 2550000
flags = 1
data = length 88, hash BC15BC88
sample 73:
time = 2560000
flags = 1
data = length 91, hash A8361A51
sample 74:
time = 2570000
flags = 1
data = length 87, hash 4AFDB5F2
sample 75:
time = 2580000
flags = 1
data = length 87, hash 6447F8CB
sample 76:
time = 2590000
flags = 1
data = length 89, hash 48305229
sample 77:
time = 2600000
flags = 1
data = length 87, hash 8741D9E7
sample 78:
time = 2610000
flags = 1
data = length 120, hash 761F020C
sample 79:
time = 2620000
flags = 1
data = length 139, hash AECE2E57
sample 80:
time = 2630000
flags = 1
data = length 166, hash 6288797A
sample 81:
time = 2640000
flags = 1
data = length 144, hash 437821A0
sample 82:
time = 2650000
flags = 1
data = length 113, hash FCCBEDF1
sample 83:
time = 2660000
flags = 1
data = length 108, hash C4040614
sample 84:
time = 2670000
flags = 1
data = length 125, hash E29064C2
sample 85:
time = 2680000
flags = 1
data = length 126, hash D42D24FF
sample 86:
time = 2690000
flags = 1
data = length 122, hash 30AF267D
sample 87:
time = 2700000
flags = 1
data = length 122, hash 45CEC1FB
sample 88:
time = 2710000
flags = 1
data = length 134, hash 59143FE2
sample 89:
time = 2720000
flags = 1
data = length 134, hash BD52A84
sample 90:
time = 2730000
flags = 1
data = length 120, hash 745C3714
sample 91:
time = 2740000
flags = 1
data = length 126, hash 505E117B
tracksEnded = true
seekMap:
isSeekable = true
duration = 2747500
getPosition(0) = [[timeUs=0, position=250]]
getPosition(1) = [[timeUs=1, position=250]]
getPosition(1373750) = [[timeUs=1373750, position=250]]
getPosition(2747500) = [[timeUs=2747500, position=250]]
numberOfTracks = 1
track 0:
total output bytes = 126
sample count = 1
format 0:
sampleMimeType = audio/opus
channelCount = 2
sampleRate = 48000
metadata = entries=[VC: encoder=Lavf54.20.4]
initializationData:
data = length 19, hash BFE794DB
data = length 8, hash CA22068C
data = length 8, hash 79C07075
sample 0:
time = 2741000
flags = 1
data = length 126, hash 505E117B
tracksEnded = true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment