google/ExoPlayer · error · java.util.concurrent.TimeoutException

TimestampAdjuster failed to initialize in " + timeoutMs + "

Error message

TimestampAdjuster failed to initialize in " + timeoutMs + " milliseconds

What it means

TimestampAdjuster.awaitInitializedBlocksOrTimeout (via initialize or getFirstSampleTimestampUs paths) throws java.util.concurrent.TimeoutException when the adjuster is not initialized within the caller-supplied timeoutMs: the wait loop accumulates elapsed realtime via wait(remainingTimeoutMs) and, on total wait >= timeoutMs with isInitialized() still false, throws with the timeout in the message. Initialization happens when the first 'reference' sample timestamp is set (usually the first frame after a seek or stream start); until then all other threads block.

Source

Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java:143

      return;
    } else if (canInitialize) {
      this.nextSampleTimestampUs.set(nextSampleTimestampUs);
    } else {
      // Wait for another calling thread to complete initialization.
      long totalWaitDurationMs = 0;
      long remainingTimeoutMs = timeoutMs;
      while (!isInitialized()) {
        if (timeoutMs == 0) {
          wait();
        } else {
          checkState(remainingTimeoutMs > 0);
          long waitStartingTimeMs = SystemClock.elapsedRealtime();
          wait(remainingTimeoutMs);
          totalWaitDurationMs += SystemClock.elapsedRealtime() - waitStartingTimeMs;
          if (totalWaitDurationMs >= timeoutMs && !isInitialized()) {
            String message =
                "TimestampAdjuster failed to initialize in " + timeoutMs + " milliseconds";
            throw new TimeoutException(message);
          }
          remainingTimeoutMs = timeoutMs - totalWaitDurationMs;
        }
      }
    }
  }

  /**
   * Returns the value of the first adjusted sample timestamp in microseconds, or {@link
   * C#TIME_UNSET} if timestamps will not be offset or if the adjuster is in shared mode.
   */
  public synchronized long getFirstSampleTimestampUs() {
    return firstSampleTimestampUs == MODE_NO_OFFSET || firstSampleTimestampUs == MODE_SHARED
        ? C.TIME_UNSET
        : firstSampleTimestampUs;
  }

  /**

View on GitHub (pinned to dd430f7053)

Solutions

  1. Increase or disable the timeout: construct TimestampAdjuster with a larger/0 timeout (0 = wait indefinitely), or the media3 API that exposes the initialization timeout for the shared-timestamp-adjuster path
  2. Ensure the owner/first track actually produces a sample: check upstream MediaSource errors, decoder init exceptions, and that the seek position maps to a real sample (SeekMap)
  3. Release order: when stopping playback before first frame, release renderers so waiting threads are interrupted rather than timing out
  4. For custom pipeline code, set the initial timestamp explicitly via TimestampAdjuster.reset with a first sample or use setTimestampOffsetSync so waiters never block on first data

Example fix

// before
TimestampAdjuster adjuster = new TimestampAdjuster(TIMEOUT_MS); // short timeout, blocks timeout
long base = adjuster.getFirstSampleTimestampUs();
// after
TimestampAdjuster adjuster = new TimestampAdjuster(0 /* wait indefinitely */);
long base = adjuster.getFirstSampleTimestampUs(); // or feed owner sample promptly
Defensive patterns

Strategy: retry

Validate before calling

if (!adjuster.isInitialized()) {
  // ensure the owner track will deliver a sample, or initialize synchronously
}

Try / catch

try { adjuster.getFirstSampleTimestampUs(); }
catch (TimeoutException e) { /* release renderers / retry stream init / surface decoder error */ }

Prevention

When it happens

Trigger: Shared-mode TimestampAdjuster groups (SPMC video+audio): the thread that should call adjustSampleTimestamp first (owner) never does because its decoder stalled, the seek target track never produced a frame, or the owner's renderer was disabled; dependent tracks then time out after their configured timeoutMs (passed via DefaultRenderersFactory / setEnableDecoderFallback paths or experimental settings).

Common situations: Live streams where the initial segment lacks the expected track; DRM/decoder init failure on the owner track; content with audio-only or video-only periods where the other adjuster never initializes; aggressive timeouts set by an app (e.g. via DefaultRenderersFactory's timestamp adjuster initialization timeout) interacting with slow device decoders; blocked threads at app shutdown if release happens before first sample.

Understand the failure class

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/3f9c4c51fa5f6c8a. Report an issue: GitHub.