From f4bf376e891bb0326b4c569cecec825e870aec92 Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Tue, 6 Jun 2023 14:13:44 +0000 Subject: [PATCH] Defer outputting the metadata sample when TimestampAdjuster isn't initialized The sample timestamp carried by the emsg box can have a significant delta when comparing to the earliest presentation timestamp of the segment. Using this timestamp to intialize the timestamp offset in TimestampAdjuster will cause the media sample to have a wrong adjusted timestamp. So we should defer adjusting the metadata sample timestamp until the TimestampAdjuster is initialized with a real media sample. PiperOrigin-RevId: 538172841 --- RELEASENOTES.md | 3 +++ .../media3/common/util/TimestampAdjuster.java | 12 ++++++++---- .../media3/extractor/mp4/FragmentedMp4Extractor.java | 7 +++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9913494af4..690822059a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -14,6 +14,9 @@ * Parse EXIF rotation data for image inputs. * Track Selection: * Extractors: + * FMP4: Fix issue where `TimestampAdjuster` initializes a wrong timestamp + offset with metadata sample time from emsg atom + ([#356](https://github.com/androidx/media/issues/356)). * Audio: * Audio Offload: * Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of diff --git a/libraries/common/src/main/java/androidx/media3/common/util/TimestampAdjuster.java b/libraries/common/src/main/java/androidx/media3/common/util/TimestampAdjuster.java index e1d6619bb3..7aaae7f925 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/TimestampAdjuster.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/TimestampAdjuster.java @@ -106,14 +106,13 @@ public final class TimestampAdjuster { public synchronized void sharedInitializeOrWait(boolean canInitialize, long nextSampleTimestampUs) throws InterruptedException { Assertions.checkState(firstSampleTimestampUs == MODE_SHARED); - if (timestampOffsetUs != C.TIME_UNSET) { - // Already initialized. + if (isInitialized()) { return; } else if (canInitialize) { this.nextSampleTimestampUs.set(nextSampleTimestampUs); } else { // Wait for another calling thread to complete initialization. - while (timestampOffsetUs == C.TIME_UNSET) { + while (!isInitialized()) { wait(); } } @@ -195,7 +194,7 @@ public final class TimestampAdjuster { if (timeUs == C.TIME_UNSET) { return C.TIME_UNSET; } - if (timestampOffsetUs == C.TIME_UNSET) { + if (!isInitialized()) { long desiredSampleTimestampUs = firstSampleTimestampUs == MODE_SHARED ? Assertions.checkNotNull(nextSampleTimestampUs.get()) @@ -208,6 +207,11 @@ public final class TimestampAdjuster { return timeUs + timestampOffsetUs; } + /** Returns whether the instance is initialized with a timestamp offset. */ + public synchronized boolean isInitialized() { + return timestampOffsetUs != C.TIME_UNSET; + } + /** * Converts a 90 kHz clock timestamp to a timestamp in microseconds. * diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 9427ba1263..f4705aa934 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -683,6 +683,13 @@ public class FragmentedMp4Extractor implements Extractor { pendingMetadataSampleInfos.addLast( new MetadataSampleInfo(sampleTimeUs, /* sampleTimeIsRelative= */ false, sampleSize)); pendingMetadataSampleBytes += sampleSize; + } else if (timestampAdjuster != null && !timestampAdjuster.isInitialized()) { + // We also need to defer outputting metadata if the timestampAdjuster is not initialized, + // else we will set a wrong timestampOffsetUs in timestampAdjuster. See: + // https://github.com/androidx/media/issues/356. + pendingMetadataSampleInfos.addLast( + new MetadataSampleInfo(sampleTimeUs, /* sampleTimeIsRelative= */ false, sampleSize)); + pendingMetadataSampleBytes += sampleSize; } else { // We can output the sample metadata immediately. if (timestampAdjuster != null) {