KitePlayer
The player.
One media item at a time, played through a backend that is passed in rather than discovered. Everything about what is happening is read from the four flows below; everything a caller can ask for is one of the calls below. There is nothing else, and that is the point: a member here means the engine implements it, and a feature that is not implemented is absent rather than present and ignored.
What it is made of
The state and the decisions live in one session actor, with workers beside it for demux, video decode, audio decode, audio feed, video schedule, subtitle raster and release. Each of the eight runs on a serial lane of its own, so it does one thing at a time, but a lane is not a thread: on the JVM, Android and the native targets all eight are lanes over the shared Dispatchers.Default and Dispatchers.IO pools, and on the web they share the page's one thread. This class is the outside of that. Accepted state-changing commands are actor messages: awaited calls carry one reply each, while fire-and-forget calls discard or omit theirs. The two close routes instead share one terminal result. After the actor returns, its independent close finalizer alone publishes the terminal snapshot and result. Calling from any thread or coroutine is safe and no two callers can race each other into a state neither asked for.
Which calls wait
A call that takes time suspends until it is done: open, seek, stop, the queue and chapter moves, selectTrack and the others marked suspend. play, pause and the setters only change what the player is asked to do, so they return at once and the state flows show the result. requestSeek is the seek that does not wait, for a dragged seek bar. close returns at once because AutoCloseable requires it, and closeAndAwait waits for the teardown.
State against events
state, progress and stats are state: they conflate, and a collector that misses an intermediate value still ends up correct. events is for occurrences that are the information themselves. Nothing a caller must count is delivered as an event, and a failure is on state as well as in events, because a collector that subscribes after the failure would otherwise never learn of it.
Errors
A suspending call that fails throws PlaybackException, which carries the typed PlaybackError. Cancelling the caller's coroutine stays a CancellationException and is never turned into a playback failure. A call made in the wrong order, or with a value outside its documented range, throws IllegalStateException, IllegalArgumentException or UnsupportedOperationException instead: those are mistakes in the calling code, not failures of the media or the device, and telling them apart is what lets an application decide whether to show the user anything.
What is not here
External subtitles, filter chains, the open-option escape hatch, chapters, the queue, shuffle, a secondary subtitle track, frame stepping and the gapless queue handoff were on this list and are all here now: addExternalSubtitle, MediaItem.videoFilter, MediaItem.openOptions, chapterAt with seekToChapter, openQueue with next and previous, setShuffle, selectSecondarySubtitle, stepFrame, setBalance and QueueConfig. A member that describes something still unbuilt says so in its own documentation.
Properties
Warnings, failures and the occurrences worth naming. Replays nothing to a late collector.
The KeyframeChoice the next keyframe seek takes; see setKeyframeChoice.
The same events as events, with none ever dropped (#414).
Everything about the player that changes rarely. Position is deliberately not in it.
Diagnostics, republished on PlayerConfig.statsInterval.
The subtitle cues showing right now, in draw order. Empty when none are.
A count of the transport commands callers gave: play, pause, stop, and every open or queue move. It only grows. A guard that pauses the player on its own, for a call or for the screen going off, reads it right after its pause and resumes later only if the count has not moved, so it never undoes a play or a pause somebody made in between.
Functions
Loads a subtitle FILE during playback, appends it to PlayerSnapshot.tracks as an external track, selects it, and returns its id once it is really showing.
Inserts one item. See the list overload for everything else.
Hands every block of decoded audio to tap on its way to the speaker. Legal at any time, including before anything is open and while playing, and the tap stays attached across opens until detachAudioTap. Attaching the same tap twice changes nothing.
Attaches a renderer, or replaces the one attached. Legal at any time, including while playing.
attachRenderer, returning once the engine has attached renderer or refused it.
A coherent audible clock for visualisation and synchronised UI, independent of the seek bar.
Suspends until this player is asked to close, through close or closeAndAwait from anywhere, and returns at once when that already happened.
Returns the newest presented frame as an owned, software-readable copy: the documented use of io.github.yuroyami.kiteplayer.spi.SoftwareReadableFrame.
Removes every item except the one playing, which is left at index 0 and is not reopened.
Closes the player and returns only after the session actor and teardown have completed.
Stops handing audio to tap. A tap that is not attached is ignored.
Detaches the current renderer. Playback continues without a picture. See attachRenderer.
Detaches expected only while it is still the attached renderer; a stale call is a no-op. This is the safe form for presentation code whose teardown can race a newer attach.
Everything a bug report needs, in one string: the resolved configuration, the backends by name, tracks and selections, the three published snapshots, the KD artifacts attached to the session, and the bounded warning history. Safe from any thread at any moment, including after a failure, which is when it is usually wanted.
Where playback is, as one value: the queue, the item, the position and every setting the player holds, including balance, the equaliser, and every picture and subtitle setting. Store it however you like and hand it back to restore. A player that opened a single item reports a queue of one; a player with nothing open reports an empty queue and an index of -1, which restore refuses.
Seeks to the start of the chapter after the one holding the current position, with seek's contract. Does nothing at the last chapter, and in media with no chapter table.
Opens the playlist file at uri as the queue, starting at startIndex: readPlaylist and then openQueue, with what each throws.
Seeks to the start of the current chapter, or of the previous one when less than three seconds into the current one: the rule every music player has, so a second press means "the one before". At the first chapter both readings restart it. In a gap between chapters the last chapter to have started counts as the current one. Does nothing in media with no chapter table.
Draws the picture on screen again (#438), for a surface that was replaced and came back empty, as an Android view's does when its application returns to the foreground. A paused or ended player decodes the picture it shows once more and presents it, and its position and its status stay as they are, so an ended player stays PlaybackStatus.Ended and play still starts it from the beginning. A playing player needs nothing, because its next frame comes on its own, and a source that cannot seek cannot decode a past picture again. Fire and forget.
Reads an external subtitle track's file again, in encoding, and puts the new reading in place of the old one (#515).
Removes the item at index.
Asks for a seek and returns at once, merging requests that arrive faster than the pipeline can serve them.
Takes the player back to a memento: applies every setting, opens its queue at its index, goes to its position, then picks the audio and subtitle tracks by language where the new container has them. Ends paused, like every open. A track the memento names and the container lacks leaves the container's own choice in place.
The old name of requestSeek. It never waited, which the name did not say.
Seeks to the start of a subtitle line and returns it once the line's first frame is on screen, with seek's contract (#491). offset 0 is the line showing, or the last line before now when none shows, so a learner who missed a line hears it again; -1 and +1 are the previous and the next line. Lines are the selected subtitle track's cues as far as the player has read them, every cue of an external file and those an embedded track has delivered, at the times they show with the subtitle delay. Repeated calls move one line each, a paused player included, because each counts from where the last one went.
Plays the channel numbered number of Tracks.programs, or chooses one again, the first with a picture, when number is null (#505). The media opens again on that channel, through the same rebuild as a video track change, and the picture, the sound and the subtitles are all chosen again from its tracks by the usual rules, unless a track change waiting beside it asks for one. The player keeps playing or stays paused. The choice is kept on the item as DemuxPolicy.program, so a later rebuild keeps it too.
Shows a second subtitle track where SubtitleConfig.secondaryPlacement puts it, at the top of the picture by default, or clears it with null. SubtitleConfig.secondaryLanguages can choose one at each open instead.
Selects a track, or deselects the kind entirely with a null track, and says what happened.
Plays the variant at index of Tracks.variants, or chooses one again by the item's DemuxPolicy when index is null. The media opens again on that variant at the current position, through the same rebuild as a video track change, and keeps playing or stays paused. The choice is kept on the item, so a later rebuild keeps it too.
Delays the sound against the picture by value, mpv's audio-delay sign. A positive value presents every video frame that much earlier. It is for sound that reaches the ear early.
Sets the stereo balance: -1 is hard left, 0 is centre, 1 is hard right.
Lowers the sound by level, a factor from 1, no change, down to 0, without touching the volume. The media session guards use it to duck under a notification: the volume the listener set, and every control bound to it, stays where it is. A duck multiplies the volume, so it can only make the sound quieter.
Sets the ten-band equaliser. EqualizerSettings.Flat turns it off.
Makes playback follow clock, or nothing when it is null. Legal at any time, and it lasts across items until it is replaced. See ExternalClock for how each answer is followed, and PlayerConfig.externalClock for the largest speed change it may use.
Draws only the forced pictures of a Blu-ray or DVD subtitle track, or every picture again (#513), as mpv's sub-forced-events-only. SubtitleConfig.forcedPicturesOnly says what that means and is where the player starts. Applies to the subtitles showing now, with no reselection; published as PlayerSnapshot.forcedPicturesOnly.
Sets how HDR video reaches the screen. A renderer that can show HDR applies the change to its next frame, and the Android Surface path to its next open. PlayerSnapshot.videoDynamicRange says what the screen shows.
Replaces the title, the artist and the album of the item that is playing, without opening it again (#423), for a radio that publishes its song list somewhere else, or a stream whose details the application learns later. Null clears a field. The item in PlayerSnapshot.media and in the queue changes, and the media session and the notification follow.
Chooses which keyframe a SeekMode.Keyframe seek lands on, for the seeks asked for from now on. KeyframeChoice.InSeekDirection is what a skip button wants in a file whose keyframes are far apart. The precise modes are not affected.
Positions to announce with PlayerEvent.MarkerReached as playback crosses them. Sorted by the engine and replaced wholesale, so an empty list clears them. They belong to the player rather than to the item: a new item starts with every marker armed.
Turns the night mode on or off (#442): the quiet parts of the sound are brought up and the loud parts down, so speech can be followed at a volume that does not wake the house, as a receiver's night mode or mpv's dynaudnorm does. It acts on the output after the downmix, with a gentle attack and release, and never passes full scale. Off, the default, costs nothing and leaves every sample as it was. A change glides in and out, so it never clicks, and is heard once the audio already buffered has played, as a setBalance change is. Published as PlayerSnapshot.nightMode.
Moves the pitch by semitones, up or down to an octave, without changing how fast the media plays (#465), for a singer practising in another key or a learner following a voice that is hard to hear, as VLC's pitch control does. It works with any setSpeed, and with setPreservePitch false the speed's own pitch change adds to it. The position, the clock and the picture's sync are untouched, because a pitch changes no timing. Zero, the default, costs nothing and leaves every sample as it was. A change has no seam and no gap, and is heard once the audio already buffered has played, as a speed change is. Published as PlayerSnapshot.pitchSemitones.
Chooses whether setSpeed keeps pitch.
Sets how much work the renderer spends on the picture beyond decoding it correctly: dithering, debanding, and which kernel resamples the frame.
Plays the queue in a shuffled order, or puts it back in the order it was given.
Shortens the silent stretches of the sound (#429), for a podcast or an audiobook: every pause longer than a fifth of a second is cut down to a fifth of a second, with a short fade at each side of the cut so it never clicks, as Media3's skip silence and the trim silence of podcast players do. A pause shorter than that, between two words, is left as it is. It works with any setSpeed, and the two together are what a listener in a hurry turns on.
Stops playback later, fading the sound down first. Null cancels an armed timer.
Sets what the two front speakers play (#462): each its own side, both the average of the two, both the left or both the right, or the two swapped. It acts after the downmix, so a surround film folded to two speakers obeys it too. A change crossfades, so it never clicks, and is heard once the audio already buffered has played, as a setBalance change is. Published as PlayerSnapshot.stereoMode.
Shifts subtitle timing by value. Positive shows cues later. Applies to the cues already on screen at the next pass, no reopen and no reselection.
Moves the subtitles up the screen: value is where the implicit bottom stack anchors, as a fraction of the viewport height, mpv's sub-pos over 100. 1.0, the default, is the ordinary bottom edge; 0.9 lifts the stack a tenth of the screen, which is what a viewer with a player bar over their subtitles wants. Explicitly positioned cues are the author's word and do not move. The active cues re-rasterise immediately; published as PlayerSnapshot.subtitlePosition.
Keeps subtitles inside the safe area of the output: value holds an inset from each edge, each a fraction of the output. The built-in text drawing lays cues out inside what is left, as if it were the whole output, and a typeset ASS track keeps its author's placement. The active cues re-rasterise immediately. docs/subtitle-placement.md has the whole rule.
Scales subtitle text over the authored size. The active cues re-rasterise at the new size immediately; 1.0 is the authored size.
Overrides the authored subtitle style with the viewer's own, or clears the override.
Sets the live picture controls: brightness, contrast, saturation, hue and gamma, mpv's eq.
Parks or resumes video decoding in place, without reopening anything.
Sets the framing controls: a forced display aspect, magnification, and pan, mpv's video-aspect-override, video-zoom and video-pan-x/-y.
Sets the volume, from silence at 0 to unity at 1, and up to AudioConfig.volumeCeiling when that has been raised to allow a boost.
Starts copying what plays into a Matroska file at path, with no re-encode.
Steps a PAUSED player by exactly one decoded frame and returns with it on screen.
Shifts the subtitle delay so the line offset lines away starts now (#491), for subtitles that are out of sync by a line or so: +1 brings the next line forward to now, -1 holds the previous one back to now, and 0 starts the line showing now. The result is the new delay, published as PlayerSnapshot.subtitleDelay like a setSubtitleDelay.
Stops the recording and finishes its file. Does nothing when no recording runs.
The dump plus a platform block, with every path trimmed to its basename: what a user pastes into a bug report without leaking their filesystem.
The seek bar picture for position of the item that plays, or null when the item carries no pictures or none stands for that position (#433). The pictures come from the item's MediaItem.thumbnails file, or else from the stream itself, a DASH thumbnail set or an HLS image playlist, which Tracks.thumbnails lists. An image downloads only when it is asked for, and a few recent ones are kept, so asking at every step of a scrub costs one download for each image. The picture's times count from the item's start, as position does.
The last warnings this player emitted, oldest first, capped.