PlaybackStats
Diagnostics, for an overlay or a bug report.
The counters here are monotonic totals, not per-interval values, and that is deliberate. A consumer that wants a rate diffs two snapshots. Publishing occurrences as events instead would be wrong: a state feed coalesces by design, so an event stream of "a frame was dropped" is exactly the kind of thing that silently loses entries. libmpv learned this the hard way and had to add three logical timestamps per observer to compensate.
Scheduler counters, not renderer counters
Every frame figure here is a decision the engine's schedule made or an outcome it can see with its own eyes: submittedFrames means a renderer accepted the frame, headlessFrames means there was no renderer to accept it, and droppedFramesLate and repeatedFrames are the schedule's own choices about pacing. None of them says that a pixel reached a display.
What was drawn is the renderer's own count, and it is deliberately not mirrored here. A renderer that takes a frame may still supersede it with a newer one or fail to draw it, and it counts those outcomes itself: the Core Graphics renderer in kiteplayer-output publishes presentedFrames, supersededFrames and failedFrames, whose sum is what the engine submitted to it. Read the pair of numbers side by side: submitted against drawn is the difference between the engine keeping up and the output keeping up, and copying the second group into this class would only invent a figure the engine cannot know, because no member of the renderer interface reports one. Per-submission terminal feedback is the tracked work that would change that.
Constructors
Properties
What the audio sink reports as handed over but not yet audible.
Audio frames the output's peak limiter turned down because they would have passed full scale.
What the container declares as its overall bit rate, in bits per second.
Wall time one decoded video frame took, median over the last 240 frames.
Wall time one decoded video frame took, 95th percentile over the last 240 frames.
Events KitePlayer.events could not take because its buffer was full.
Packets thrown away before the decoder ever saw them.
Frames the schedule decided were too late to show. A scheduler decision, not a renderer one.
Frames the schedule presented with no renderer attached.
Bytes pulled over the last stats interval, per second. Same caveat as ioBytesTotal.
Bytes pulled from the media source since this player opened.
How late frames reached the renderer against the schedule's own target, 95th percentile over the last 240 accepted frames. Negative is early.
Frames a renderer was handed and refused, for example because its surface is gone.
Frames the schedule showed for longer than their own duration, counted once each.
Frames a renderer accepted from the schedule.