WebCanvasVideoRenderer

class WebCanvasVideoRenderer(canvas: JsAny, painter: WebFramePainter, keepDisplayAwake: Boolean = true) : VideoRenderer

Draws frames onto an HTML canvas.

The two canvases, and why there are two

The frame is written into an offscreen canvas at its own stored size with putImageData, and that canvas is then drawn onto the visible one with drawImage. putImageData alone would be one step shorter and cannot do the job: it writes raw pixels and ignores every transform, so letterboxing, zoom, pan and rotation would all be impossible and the picture would only ever appear at its stored size in the top-left corner. drawImage respects the context transform, which is what makes the geometry law below apply at all.

What it does not do

This is the first web renderer tier: a canvas under the Compose controls, not the single Compose surface where clip, alpha and rotation apply to the video pixels themselves. That is tier two and is not this. There is also no hardware path: the wasm decoder is software by construction, so supportedHardwareSurfaces is empty and always will be on this renderer. The picture controls in io.github.yuroyami.kiteplayer.VideoAdjustments are not applied either.

Not thread-safe, and on the web that is not a constraint: there are no threads. present is already suspend and runs on the event loop with no worker, no dispatcher and no runBlocking.

Parameters

keepDisplayAwake

whether the page's screen stays awake while pictures are drawn, and for two seconds after the last one (#238), through WebDisplayAwake. In a worker it does nothing, because a worker has no screen; the page's KitePlayerWorker holds it there.

Constructors

Link copied to clipboard
constructor(canvas: JsAny, painter: WebFramePainter, keepDisplayAwake: Boolean = true)
constructor(canvas: JsAny, painter: WebFramePainter)

Keeps the original painter constructor, including trailing-lambda calls.

Properties

Link copied to clipboard
open override val events: Flow<RendererEvent>
Link copied to clipboard
Link copied to clipboard
open override val outputSize: VideoSize?

The canvas's backing store, in device pixels: the size setViewport set, or the canvas's own size before that. The engine lays subtitles out for it, so text lands in the bars and at the canvas's own pixel size (docs/subtitle-placement.md, rule 2).

Link copied to clipboard

Diagnostics, in the same three counts the Android renderer keeps.

Link copied to clipboard
Link copied to clipboard
open val showsHdr: Boolean

Functions

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

Takes the picture off (#530): the canvas shows its background, transparent as between the bars, with the cues over it, until the next picture. The stage keeps its storage for that picture, but nothing draws from it again.

Link copied to clipboard
open override fun close()
Link copied to clipboard
open suspend override fun present(frame: VideoFrame, targetNanos: Long): Boolean
Link copied to clipboard
open fun setAdjustments(adjustments: VideoAdjustments)
Link copied to clipboard
open fun setHdrPolicy(policy: HdrPolicy)
Link copied to clipboard
open suspend override fun setOverlay(overlay: SubtitleOverlay?)
Link copied to clipboard
open fun setRenderQuality(quality: RenderQuality)
Link copied to clipboard
open override fun setScaleMode(mode: VideoScale)
Link copied to clipboard
open override fun setTransform(transform: VideoTransform)
Link copied to clipboard
open override fun setViewport(width: Int, height: Int, scale: Float)

The canvas's BACKING STORE is sized here, which is the only place that can be right.

Link copied to clipboard

Software only. A browser's own hardware decoder is WebCodecs' path and does not arrive here.

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

Any software format, because the painter converts rather than this class. PlayerPixelFormat.Opaque is refused: it means a hardware frame, nothing on the web produces one for this renderer, and answering true would let a mis-wired decoder fail per frame instead of at attach.

Link copied to clipboard
Link copied to clipboard
open override fun vsyncIntervalNanos(): Long?

Null, honestly. requestAnimationFrame follows the display and a page can read screen.refreshRate nowhere portable, so a guess here would be a number the schedule trusts. Rule 4 says the cost of null is smoothness on a high refresh display and nothing else.