diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java
index 5404a20545..5fc84bd1aa 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/BaseRenderer.java
@@ -39,7 +39,31 @@ import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
-/** An abstract base class suitable for most {@link Renderer} implementations. */
+/**
+ * An abstract base class suitable for most {@link Renderer} implementations.
+ *
+ *
It converts many of the state transitions explained in {@link Renderer} docs to protected
+ * callbacks and provides utilities to access current state values without tracking them manually:
+ *
+ *
+ * - {@link #onInit}, {@link #onEnabled}, {@link #onStarted}, {@link #onStopped}, {@link
+ * #onDisabled}, {@link #onReset} and {@link #onRelease} are called for the corresponding
+ * {@link Renderer} method.
+ *
- {@link #onStreamChanged} is called for both the initial stream set via {@link #enable} as
+ * well as subsequent streams set via {@link #replaceStream}.
+ *
- {@link #onPositionReset} is called for the initial reset via {@link #enable} as well as
+ * subsequent resets via {@link #resetPosition}.
+ *
- The current {@link SampleStream} can be read with {@link #readSource} or skipped with
+ * {@link #skipSource}. {@link #isSourceReady()} returning {@code true} indicates that samples
+ * are available to be read.
+ *
- Current state is available with additional getter methods like {@link
+ * #getLastResetPositionUs()}, {@link #getPlayerId()}, {@link #getTimeline()}.
+ *
- Exceptions can be created with {@link #createRendererException} to fill in additional
+ * metadata about the renderer automatically.
+ *
- The renderer can call {@link #onRendererCapabilitiesChanged()} to tell the player of a
+ * change in its capabilities, which may lead to new tracks being selected for playback.
+ *
+ */
@UnstableApi
public abstract class BaseRenderer implements Renderer, RendererCapabilities {
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java
index 368d81c929..388fe1ad98 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java
@@ -58,6 +58,90 @@ import java.util.List;
*
+ *
+ *
Format support
+ *
+ * The player will select a renderer based on the {@link RendererCapabilities} returned from {@link
+ * #getCapabilities()}. If the capabilities indicate support for a {@link Format}, the renderer is
+ * expected to handle {@link SampleStream} instances containing samples of this {@link Format}.
+ *
+ * Resource management
+ *
+ * Renderers should acquire resources like codecs when entering {@link #STATE_ENABLED} for the
+ * first time. As the renderer may transition quickly to and from {@link #STATE_DISABLED}, it is
+ * recommended to hold onto resources even when entering {@link #STATE_DISABLED}. The player will
+ * explicitly call {@link #reset()} if the renderer is no longer needed, at which point any acquired
+ * limited resources should be released.
+ *
+ *
Configuration changes
+ *
+ * As renderers are created once in the lifetime of a player, they may need to be re-configured
+ * later based on user settings or other environmental changes. This is generally done by handling
+ * {@linkplain PlayerMessage player messages} in {@link #handleMessage}. There are many predefined
+ * common reconfigurations in {@link MessageType}, but custom renderers can add their own handling
+ * as needed.
+ *
+ *
Reading and rendering samples
+ *
+ * The renderer receives a {@link SampleStream} to read from when {@linkplain #enable enabled}.
+ * When seamlessly transitioning from one item to another, the renderer may get new {@link
+ * SampleStream} instances via {@link #replaceStream}. Note that {@link #replaceStream} may be
+ * called as soon as the {@linkplain C#BUFFER_FLAG_END_OF_STREAM end-of-stream signal} has been read
+ * from the current {@link SampleStream} to allow reading new samples as early as possible. At this
+ * point, the renderer may still be processing samples from the previous stream(s). Once the current
+ * stream has been {@linkplain #setCurrentStreamFinal() marked as final}, no further calls to {@link
+ * #replaceStream} are allowed to happen without first {@link #disable disabling} the renderer
+ * again.
+ *
+ *
The player will regularly call {@link #render} to let the renderer make progress. Once the
+ * renderer has prepared its internal pipeline to handle continuous playback progress, it should
+ * report itself as {@link #isReady()}. The player will only transition the renderer to {@link
+ * #STATE_STARTED} if it reports itself as ready. If the renderer is blocked from making progress,
+ * it should return {@code false} from {@link #isReady()}, which will result in a {@link #stop()}
+ * operation back to {@link #STATE_ENABLED}.
+ *
+ *
As long as it is in {@link #STATE_STARTED}, the renderer is expected to actively output the
+ * data it is processing in line with the current playback position passed to {@link #render}. The
+ * only exception is the very first sample (for example the first video frame), that is allowed to
+ * be output in {@link #STATE_ENABLED} if the {@code mayRenderStartOfStream} flag was set in {@link
+ * #enable} or later set via {@link #enableMayRenderStartOfStream()}.
+ *
+ *
Once the renderer finished all processing it needs to do (that is, no further call to {@link
+ * #render} is needed) and the current stream is {@linkplain #isCurrentStreamFinal() final}, it
+ * should report itself as {@link #isEnded()}.
+ *
+ *
Timestamps and offsets
+ *
+ * The renderer deals with potentially multiple consecutive input streams and has to handle
+ * position updates and stream transitions. This means there are multiple types of timestamps and
+ * offsets relevant in the context of this class:
+ *
+ *
+ * - {@link SampleStream} timestamp: The timestamp associated with each sample read from
+ * a {@link SampleStream}. This corresponds to the time in the {@link Timeline.Period} this
+ * stream belongs to. The current playback context can be obtained by using the {@link
+ * #setTimeline Timeline} and the {@link MediaPeriodId} provided together with each {@link
+ * SampleStream}.
+ *
- Renderer timestamp: The timestamp of the overall playback. This is a continuously
+ * increasing value across all input sample streams and what it passed to {@link #render} as
+ * the current {@code positionUs}.
+ *
- Stream offset: The constant offset between the current {@link SampleStream}
+ * timestamps and the renderer timestamp. This value is provided when setting a new
+ * stream in {@link #enable} or {@link #replaceStream}.
+ *
- Stream start position: The renderer timestamp at which the current {@link
+ * SampleStream} first starts playing. This value is provided when setting a new stream in
+ * {@link #enable} or {@link #replaceStream}. Note that it may be different from the Stream
+ * offset as streams can start at non-zero {@link SampleStream} timestamps.
+ *
- Reset position: The Renderer timestamp at which continuous playback last
+ * started. This is either the stream start position provided in {@link #enable}, or
+ * the position set in any subsequent {@link #resetPosition} call. Note that this value is not
+ * changed when playback transitions seamlessly to a new stream after calling {@link
+ * #replaceStream}. After any position reset, the samples read next are those needed to start
+ * playback at the intended reset position. In some cases, the renderer will encounter
+ * output data before the intended reset position (for example video frames starting
+ * from the previous keyframe). In these cases, the renderer should drop data internally and
+ * only start producing output at the intended reset position.
+ *
*/
@UnstableApi
public interface Renderer extends PlayerMessage.Target {