Skip to content

Commit

Permalink
Permit duplicate Opus headers
Browse files Browse the repository at this point in the history
This reinstates the permissive behaviour removed by
fe7e5b8

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
(cherry picked from commit 1282175)
  • Loading branch information
icbaker authored and marcbaechinger committed May 31, 2022
1 parent 7671e50 commit f6f4bf5
Show file tree
Hide file tree
Showing 9 changed files with 3,448 additions and 5 deletions.
4 changes: 3 additions & 1 deletion RELEASENOTES.md
Expand Up @@ -56,7 +56,9 @@
subtitle format.
* Extractors:
* Matroska: Parse `DiscardPadding` for Opus tracks.
* Parse bitrates from `esds` boxes.
* MP4: Parse bitrates from `esds` boxes.
* Ogg: Allow duplicate Opus ID and comment headers
([#10038](https://github.com/google/ExoPlayer/issues/10038)).
* UI:
* Fix delivery of events to `OnClickListener`s set on `PlayerView` and
`LegacyPlayerView`, in the case that `useController=false`
Expand Down
Expand Up @@ -15,7 +15,6 @@
*/
package androidx.media3.extractor.ogg;

import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;

import androidx.annotation.Nullable;
Expand All @@ -39,10 +38,20 @@
'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()));
Expand All @@ -57,9 +66,15 @@ protected boolean readHeaders(ParsableByteArray packet, long position, SetupData
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)
Expand All @@ -72,6 +87,15 @@ protected boolean readHeaders(ParsableByteArray packet, long position, SetupData
// 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(
Expand Down
Expand Up @@ -43,6 +43,13 @@ public void opus() throws Exception {
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);
Expand Down

0 comments on commit f6f4bf5

Please sign in to comment.