diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c8a444b4562..9c7c2924c2f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,12 @@ # Release notes +### dev-v2 (not yet released) + +* Extractors: + * FMP4: Fix issue where emsg sample metadata could be output in the wrong + order for streams containing both v0 and v1 emsg atoms + ([#9996](https://github.com/google/ExoPlayer/issues/9996)). + ### 2.17.0 (2022-02-24) * Core library: diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 48d439126e0..622cc72725c 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -664,14 +664,23 @@ private void onEmsgLeafAtomRead(ParsableByteArray atom) { emsgTrackOutput.sampleData(encodedEventMessage, sampleSize); } - // Output the sample metadata. This is made a little complicated because emsg-v0 atoms - // have presentation time *delta* while v1 atoms have absolute presentation time. + // Output the sample metadata. if (sampleTimeUs == C.TIME_UNSET) { - // We need the first sample timestamp in the segment before we can output the metadata. + // We're processing a v0 emsg atom, which contains a presentation time delta, and cannot yet + // calculate its absolute sample timestamp. Defer outputting the metadata until we can. pendingMetadataSampleInfos.addLast( - new MetadataSampleInfo(presentationTimeDeltaUs, sampleSize)); + new MetadataSampleInfo( + presentationTimeDeltaUs, /* sampleTimeIsRelative= */ true, sampleSize)); + pendingMetadataSampleBytes += sampleSize; + } else if (!pendingMetadataSampleInfos.isEmpty()) { + // We also need to defer outputting metadata if pendingMetadataSampleInfos is non-empty, else + // we will output metadata for samples in the wrong order. See: + // https://github.com/google/ExoPlayer/issues/9996. + pendingMetadataSampleInfos.addLast( + new MetadataSampleInfo(sampleTimeUs, /* sampleTimeIsRelative= */ false, sampleSize)); pendingMetadataSampleBytes += sampleSize; } else { + // We can output the sample metadata immediately. if (timestampAdjuster != null) { sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs); } @@ -1457,19 +1466,30 @@ private boolean readSample(ExtractorInput input) throws IOException { return true; } + /** + * Called immediately after outputting a non-metadata sample, to output any pending metadata + * samples. + * + * @param sampleTimeUs The timestamp of the non-metadata sample that was just output. + */ private void outputPendingMetadataSamples(long sampleTimeUs) { while (!pendingMetadataSampleInfos.isEmpty()) { - MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst(); - pendingMetadataSampleBytes -= sampleInfo.size; - long metadataTimeUs = sampleTimeUs + sampleInfo.presentationTimeDeltaUs; + MetadataSampleInfo metadataSampleInfo = pendingMetadataSampleInfos.removeFirst(); + pendingMetadataSampleBytes -= metadataSampleInfo.size; + long metadataSampleTimeUs = metadataSampleInfo.sampleTimeUs; + if (metadataSampleInfo.sampleTimeIsRelative) { + // The metadata sample timestamp is relative to the timestamp of the non-metadata sample + // that was just output. Make it absolute. + metadataSampleTimeUs += sampleTimeUs; + } if (timestampAdjuster != null) { - metadataTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataTimeUs); + metadataSampleTimeUs = timestampAdjuster.adjustSampleTimestamp(metadataSampleTimeUs); } for (TrackOutput emsgTrackOutput : emsgTrackOutputs) { emsgTrackOutput.sampleMetadata( - metadataTimeUs, + metadataSampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, - sampleInfo.size, + metadataSampleInfo.size, pendingMetadataSampleBytes, null); } @@ -1575,11 +1595,13 @@ private static boolean shouldParseContainerAtom(int atom) { /** Holds data corresponding to a metadata sample. */ private static final class MetadataSampleInfo { - public final long presentationTimeDeltaUs; + public final long sampleTimeUs; + public final boolean sampleTimeIsRelative; public final int size; - public MetadataSampleInfo(long presentationTimeDeltaUs, int size) { - this.presentationTimeDeltaUs = presentationTimeDeltaUs; + public MetadataSampleInfo(long sampleTimeUs, boolean sampleTimeIsRelative, int size) { + this.sampleTimeUs = sampleTimeUs; + this.sampleTimeIsRelative = sampleTimeIsRelative; this.size = size; } }