{"record":{"id":"3f9c4c51fa5f6c8a","repo":"google/ExoPlayer","slug":"timestampadjuster-failed-to-initialize-in-time","errorCode":null,"errorMessage":"TimestampAdjuster failed to initialize in \" + timeoutMs + \" milliseconds","messagePattern":"TimestampAdjuster failed to initialize in \" \\+ timeoutMs \\+ \" milliseconds","errorType":"exception","errorClass":"java.util.concurrent.TimeoutException","httpStatus":null,"severity":"error","filePath":"library/common/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java","lineNumber":143,"sourceCode":"      return;\n    } else if (canInitialize) {\n      this.nextSampleTimestampUs.set(nextSampleTimestampUs);\n    } else {\n      // Wait for another calling thread to complete initialization.\n      long totalWaitDurationMs = 0;\n      long remainingTimeoutMs = timeoutMs;\n      while (!isInitialized()) {\n        if (timeoutMs == 0) {\n          wait();\n        } else {\n          checkState(remainingTimeoutMs > 0);\n          long waitStartingTimeMs = SystemClock.elapsedRealtime();\n          wait(remainingTimeoutMs);\n          totalWaitDurationMs += SystemClock.elapsedRealtime() - waitStartingTimeMs;\n          if (totalWaitDurationMs >= timeoutMs && !isInitialized()) {\n            String message =\n                \"TimestampAdjuster failed to initialize in \" + timeoutMs + \" milliseconds\";\n            throw new TimeoutException(message);\n          }\n          remainingTimeoutMs = timeoutMs - totalWaitDurationMs;\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns the value of the first adjusted sample timestamp in microseconds, or {@link\n   * C#TIME_UNSET} if timestamps will not be offset or if the adjuster is in shared mode.\n   */\n  public synchronized long getFirstSampleTimestampUs() {\n    return firstSampleTimestampUs == MODE_NO_OFFSET || firstSampleTimestampUs == MODE_SHARED\n        ? C.TIME_UNSET\n        : firstSampleTimestampUs;\n  }\n\n  /**","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/library/common/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java#L125-L161","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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","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)","Release order: when stopping playback before first frame, release renderers so waiting threads are interrupted rather than timing out","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"],"exampleFix":"// before\nTimestampAdjuster adjuster = new TimestampAdjuster(TIMEOUT_MS); // short timeout, blocks timeout\nlong base = adjuster.getFirstSampleTimestampUs();\n// after\nTimestampAdjuster adjuster = new TimestampAdjuster(0 /* wait indefinitely */);\nlong base = adjuster.getFirstSampleTimestampUs(); // or feed owner sample promptly","handlingStrategy":"retry","validationCode":"if (!adjuster.isInitialized()) {\n  // ensure the owner track will deliver a sample, or initialize synchronously\n}","typeGuard":null,"tryCatchPattern":"try { adjuster.getFirstSampleTimestampUs(); }\ncatch (TimeoutException e) { /* release renderers / retry stream init / surface decoder error */ }","preventionTips":["Feed the owner track's first sample promptly; don't leave shared adjusters uninitialized across seeks","Choose timeout 0 (wait indefinitely) when startup latency is unbounded, or a generous device-scaled timeout","On release, tear down waiting threads instead of letting them hit the timeout"],"tags":["timestamps","synchronization","timeout","playback"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}