PlaybackStats

data class PlaybackStats(val decodedVideoFrames: Long = 0, val submittedFrames: Long = 0, val headlessFrames: Long = 0, val droppedFramesLate: Long = 0, val refusedFrames: Long = 0, val droppedFramesDecode: Long = 0, val repeatedFrames: Long = 0, val audioUnderruns: Long = 0, val rebuffers: Long = 0, val droppedEvents: Long = 0, val avDrift: Duration = ZERO, val videoDecodeFps: Double = 0.0, val videoQueueDepth: Duration = ZERO, val audioQueueDepth: Duration = ZERO, val audioLatency: Duration = ZERO, val audioLatencyQuality: LatencyQuality = LatencyQuality.Unreliable, val hardwareDecode: HwdecStatus = HwdecStatus.Software, val ioBytesTotal: Long = 0, val ioBytesPerSecond: Long = 0, val decodeTimeP50: Duration = ZERO, val decodeTimeP95: Duration = ZERO, val presentLatenessP95: Duration = ZERO, val containerBitrate: Long? = null, val syncMode: SyncMode = SyncMode.Auto, val masterClock: MasterClock = MasterClock.None, val audioLimitedFrames: Long = 0)

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

Link copied to clipboard
constructor(decodedVideoFrames: Long = 0, submittedFrames: Long = 0, headlessFrames: Long = 0, droppedFramesLate: Long = 0, refusedFrames: Long = 0, droppedFramesDecode: Long = 0, repeatedFrames: Long = 0, audioUnderruns: Long = 0, rebuffers: Long = 0, droppedEvents: Long = 0, avDrift: Duration = ZERO, videoDecodeFps: Double = 0.0, videoQueueDepth: Duration = ZERO, audioQueueDepth: Duration = ZERO, audioLatency: Duration = ZERO, audioLatencyQuality: LatencyQuality = LatencyQuality.Unreliable, hardwareDecode: HwdecStatus = HwdecStatus.Software, ioBytesTotal: Long = 0, ioBytesPerSecond: Long = 0, decodeTimeP50: Duration = ZERO, decodeTimeP95: Duration = ZERO, presentLatenessP95: Duration = ZERO, containerBitrate: Long? = null, syncMode: SyncMode = SyncMode.Auto, masterClock: MasterClock = MasterClock.None, audioLimitedFrames: Long = 0)

Properties

Link copied to clipboard

What the audio sink reports as handed over but not yet audible.

Link copied to clipboard

Audio frames the output's peak limiter turned down because they would have passed full scale.

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Video clock minus the master clock at the last presented frame. Positive means video is AHEAD of the master clock.

Link copied to clipboard

What the container declares as its overall bit rate, in bits per second.

Link copied to clipboard
Link copied to clipboard

Wall time one decoded video frame took, median over the last 240 frames.

Link copied to clipboard

Wall time one decoded video frame took, 95th percentile over the last 240 frames.

Link copied to clipboard

Events KitePlayer.events could not take because its buffer was full.

Link copied to clipboard

Packets thrown away before the decoder ever saw them.

Link copied to clipboard

Frames the schedule decided were too late to show. A scheduler decision, not a renderer one.

Link copied to clipboard
Link copied to clipboard

Frames the schedule presented with no renderer attached.

Link copied to clipboard

Bytes pulled over the last stats interval, per second. Same caveat as ioBytesTotal.

Link copied to clipboard

Bytes pulled from the media source since this player opened.

Link copied to clipboard
Link copied to clipboard

How late frames reached the renderer against the schedule's own target, 95th percentile over the last 240 accepted frames. Negative is early.

Link copied to clipboard
Link copied to clipboard

Frames a renderer was handed and refused, for example because its surface is gone.

Link copied to clipboard

Frames the schedule showed for longer than their own duration, counted once each.

Link copied to clipboard

Frames a renderer accepted from the schedule.

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard