VideoRenderer

Draws frames.

The renderer owns its thread and its GPU context. The engine never touches a graphics API and never assumes which thread it is on.

The rules below are stated explicitly because this is the interface libmpv makes hardest to use correctly, and its failure mode is a deadlock rather than an error:

  1. The engine calls present from the video scheduler coroutine. A renderer may hand the work to its own thread and return at once, or do it inline. Either is correct.

  2. The renderer owns the frame from the moment present is called, including when it fails, and closes it exactly once.

  3. A renderer must never call synchronously back into the player from inside present.

  4. vsyncIntervalNanos is advisory. Returning null costs smoothness on a high refresh display and nothing else.

  5. Losing a surface is an event, not an exception. Playback continues, video frames are counted as dropped, and audio keeps playing, because a minimised window should not stop the sound.

  6. A renderer may be attached and detached at any time, including while playing. Ordinary video decoding does not depend on one existing. A renderer-coupled decoder is considered only when its renderer is attached before the media session opens; attaching later does not replace the active decoder.

Properties

Link copied to clipboard
abstract val events: Flow<RendererEvent>

Surface loss, surface return, refresh changes, colour limits, hard failure.

Link copied to clipboard

The surface this renderer draws into, in PHYSICAL pixels, or null when it cannot say.

Link copied to clipboard

True when this renderer draws the picture it holds again by itself after a change of its look: scale mode, adjustments, framing, render quality or the subtitle overlay. False makes the engine decode the held picture once more after such a change while the player is paused or ended, so the change shows at once rather than with the next frame (#463). Read at each change, so the answer may follow the picture: Android's renderers answer false while a MediaCodec frame shows, because it goes to the Surface and leaves no copy, and the surface renderer answers true while a software picture it keeps shows (#541). Defaulted to true, for a renderer that keeps its picture.

Link copied to clipboard
open val showsHdr: Boolean

True when this renderer, on the display it draws to now, shows HDR as HDR under io.github.yuroyami.kiteplayer.HdrPolicy.Auto, rather than tone mapping it (#447). The engine reads it when it chooses an adaptive stream's variant, so an HDR display gets the HDR version and a standard one the SDR version. Defaulted to false, for a renderer that always tone maps.

Functions

Link copied to clipboard
open fun accepts(shape: FrameShape): Boolean

True when this renderer can show frames of shape, asked before they exist: at open, to pass over a decoder whose frames it cannot show, and at attach, to refuse a renderer that cannot show the running decoder's frames.

Link copied to clipboard
open fun clearPicture()

No picture plays any more, so the one on screen must go.

Link copied to clipboard
expect abstract fun close()
Link copied to clipboard
abstract suspend fun present(frame: VideoFrame, targetNanos: Long): Boolean

Presents frame, aiming for targetNanos on the engine's monotonic clock.

Link copied to clipboard
open fun setAdjustments(adjustments: VideoAdjustments)

The picture controls (brightness, contrast, saturation, hue), as the engine's one colour matrix law. Told on attach and on every change, exactly like setScaleMode. Defaulted so an existing renderer keeps compiling; a renderer that draws colour overrides it.

Link copied to clipboard
open fun setHdrPolicy(policy: HdrPolicy)

How HDR video reaches the screen. A renderer that can show HDR as HDR does so under io.github.yuroyami.kiteplayer.HdrPolicy.Auto and reports RendererEvent.HdrShown. The default ignores it, for a renderer that always tone maps.

Link copied to clipboard
abstract suspend fun setOverlay(overlay: SubtitleOverlay?)

Composited above the video. Replaced wholesale rather than diffed.

Link copied to clipboard
open fun setRenderQuality(quality: RenderQuality)

How much work to spend on the picture beyond decoding it correctly.

Link copied to clipboard
open fun setScaleMode(mode: VideoScale)

How the picture should occupy the surface. Defaulted so an existing renderer keeps compiling and keeps its Fit behaviour; a renderer that can crop or stretch overrides it.

Link copied to clipboard
open fun setTransform(transform: VideoTransform)

The framing controls (aspect override, zoom, pan), folded into the same geometry pass the scale mode drives. The same delivery law as setScaleMode; defaulted the same way.

Link copied to clipboard
abstract fun setViewport(width: Int, height: Int, scale: Float)

The output surface changed size. Subtitles are laid out again after this.

Link copied to clipboard

Hardware surface kinds this renderer can present without a download to main memory.

Link copied to clipboard
abstract fun supports(format: PlayerPixelFormat): Boolean

True when this renderer can draw a frame in format at all.

Link copied to clipboard

Decoder factories that require this renderer's surface or graphics context.

Link copied to clipboard
abstract fun vsyncIntervalNanos(): Long?

Display refresh interval in nanoseconds, or null when the platform does not report it.