stats window
This commit is contained in:
+114
-17
@@ -464,16 +464,18 @@ func main() {
|
||||
displayedVideoWidth uint32 = placeholderWidth
|
||||
displayedVideoHeight uint32 = placeholderHeight
|
||||
displayedVideoStride uint32 = placeholderStride
|
||||
hasDisplayedVideo bool = false
|
||||
|
||||
fps float64
|
||||
lastIndex uint64
|
||||
dropped uint64
|
||||
frameCount uint64
|
||||
lastReport time.Time
|
||||
lastFrame time.Time
|
||||
fps float64
|
||||
lastIndex uint64
|
||||
dropped uint64
|
||||
droppedTotal uint64
|
||||
frameCount uint64
|
||||
lastReport time.Time
|
||||
lastFrame time.Time
|
||||
renderLoopDT time.Duration
|
||||
)
|
||||
lastFrame = time.Now()
|
||||
lastReport = lastFrame
|
||||
|
||||
// ImGui
|
||||
var (
|
||||
@@ -555,7 +557,6 @@ func main() {
|
||||
displayedVideoWidth = pendingFrame.Frame.Width
|
||||
displayedVideoHeight = pendingFrame.Frame.Height
|
||||
displayedVideoStride = pendingFrame.Frame.Stride
|
||||
hasDisplayedVideo = true
|
||||
hasFrame = true
|
||||
} else if frameErr != nil &&
|
||||
!errors.Is(frameErr, context.DeadlineExceeded) &&
|
||||
@@ -568,6 +569,7 @@ func main() {
|
||||
if lastIndex != 0 && shownIndex > lastIndex {
|
||||
if g := shownIndex - lastIndex - 1; g > 0 {
|
||||
dropped += g
|
||||
droppedTotal += g
|
||||
}
|
||||
}
|
||||
lastIndex = shownIndex
|
||||
@@ -585,25 +587,119 @@ func main() {
|
||||
// end of stats
|
||||
if r != nil {
|
||||
gui.BeginFrame(time.Since(lastFrame), int32(r.Extent().Width), int32(r.Extent().Height))
|
||||
snapshot, hasSnapshot := player.Controller.Snapshot()
|
||||
if showStats {
|
||||
cimgui.SetNextWindowPos(cimgui.Vec2{X: 10, Y: 10})
|
||||
cimgui.SetNextWindowSize(cimgui.Vec2{X: 200, Y: 200})
|
||||
cimgui.SetNextWindowSize(cimgui.Vec2{X: 460, Y: 510})
|
||||
cimgui.BeginV("Stats", &showStats,
|
||||
cimgui.WindowFlagsNoTitleBar|cimgui.WindowFlagsNoResize|cimgui.WindowFlagsNoScrollbar)
|
||||
cimgui.Text(fmt.Sprintf("FPS: %.1f", fps))
|
||||
cimgui.Text(fmt.Sprintf("Dropped: %d", dropped))
|
||||
cimgui.Text(fmt.Sprintf("Index: %d", shownIndex))
|
||||
if hasDisplayedVideo {
|
||||
mediaStats := player.MediaStats.Snapshot()
|
||||
cimgui.SeparatorText("Video")
|
||||
if mediaStats.Video.Available {
|
||||
label := mediaStats.Video.Label
|
||||
if label == "" {
|
||||
label = "(no label)"
|
||||
}
|
||||
cimgui.TextWrapped(fmt.Sprintf("Label: %s", label))
|
||||
if hasSnapshot && snapshot.Desired.Video.IsConfigured() {
|
||||
cimgui.TextWrapped(fmt.Sprintf("Domain: %s", snapshot.Desired.Video.Domain))
|
||||
cimgui.TextWrapped(fmt.Sprintf("UUID: %s", snapshot.Desired.Video.UUID))
|
||||
}
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Video: %dx%d",
|
||||
displayedVideoWidth,
|
||||
displayedVideoHeight,
|
||||
"Resolution: %dx%d (stride %d)",
|
||||
mediaStats.Video.Width,
|
||||
mediaStats.Video.Height,
|
||||
mediaStats.Video.Stride,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"FPS: flow %.3f | received %.1f | displayed %.1f",
|
||||
mediaStats.Video.DeclaredFPS,
|
||||
mediaStats.Video.ReceivedFPS,
|
||||
fps,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Frame dt: source %.3f ms | render loop %.3f ms",
|
||||
float64(mediaStats.Video.FrameDT.Microseconds())/1000,
|
||||
float64(renderLoopDT.Microseconds())/1000,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Index: %d | payload: %d bytes",
|
||||
mediaStats.Video.Index,
|
||||
mediaStats.Video.PayloadSize,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Dropped: %d | invalid: %d",
|
||||
droppedTotal,
|
||||
mediaStats.Video.Invalid,
|
||||
))
|
||||
} else {
|
||||
cimgui.Text("No video frames received")
|
||||
}
|
||||
|
||||
cimgui.SeparatorText("Audio")
|
||||
if mediaStats.Audio.Available {
|
||||
label := mediaStats.Audio.Label
|
||||
if label == "" {
|
||||
label = "(no label)"
|
||||
}
|
||||
cimgui.TextWrapped(fmt.Sprintf("Label: %s", label))
|
||||
if hasSnapshot && snapshot.Desired.Audio.IsConfigured() {
|
||||
cimgui.TextWrapped(fmt.Sprintf("Domain: %s", snapshot.Desired.Audio.Domain))
|
||||
cimgui.TextWrapped(fmt.Sprintf("UUID: %s", snapshot.Desired.Audio.UUID))
|
||||
}
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Format: %.3f kHz, %d channels",
|
||||
mediaStats.Audio.SampleRateHz/1000,
|
||||
mediaStats.Audio.Channels,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Batch: %d samples (%.3f ms)",
|
||||
mediaStats.Audio.SampleCount,
|
||||
float64(mediaStats.Audio.BatchDuration.Microseconds())/1000,
|
||||
))
|
||||
cimgui.Text(fmt.Sprintf("Index: %d", mediaStats.Audio.Index))
|
||||
} else {
|
||||
cimgui.Text("No audio batches received")
|
||||
}
|
||||
|
||||
cimgui.SeparatorText("Runtime")
|
||||
if hasSnapshot {
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"Topology: %s | generation %d",
|
||||
snapshot.Plan.Topology,
|
||||
snapshot.Generation,
|
||||
))
|
||||
drawCompactStatus := func(label string, unit playback.Unit) {
|
||||
status, ok := statusStore.Snapshot(unit)
|
||||
if !ok {
|
||||
cimgui.Text(fmt.Sprintf("%s: not started", label))
|
||||
return
|
||||
}
|
||||
cimgui.Text(fmt.Sprintf(
|
||||
"%s: %s (attempt %d, failed %d)",
|
||||
label,
|
||||
status.State,
|
||||
status.Attempt,
|
||||
status.FailedAttempts,
|
||||
))
|
||||
}
|
||||
switch snapshot.Plan.Topology {
|
||||
case playback.TopologySynchronized:
|
||||
drawCompactStatus("Sync", playback.UnitSync)
|
||||
case playback.TopologyIndependent:
|
||||
if snapshot.Plan.Video.Active {
|
||||
drawCompactStatus("Video", playback.UnitVideo)
|
||||
}
|
||||
if snapshot.Plan.Audio.Active {
|
||||
drawCompactStatus("Audio", playback.UnitAudio)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cimgui.Text("Playback controller is starting")
|
||||
}
|
||||
cimgui.End()
|
||||
}
|
||||
// settings & info window
|
||||
snapshot, hasSnapshot := player.Controller.Snapshot()
|
||||
videoConfigured := videoStr != ""
|
||||
audioConfigured := audioStr != ""
|
||||
if hasSnapshot {
|
||||
@@ -933,6 +1029,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
renderLoopDT = time.Since(frameStart)
|
||||
|
||||
} else {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
@@ -20,6 +20,7 @@ type playerPlayback struct {
|
||||
Commands chan playback.SessionCommand
|
||||
Video *playback.VideoBridge
|
||||
Status *playback.StatusStore
|
||||
MediaStats *playback.MediaStatsStore
|
||||
Audio playerAudioSink
|
||||
}
|
||||
|
||||
@@ -29,7 +30,10 @@ func newPlayerPlayback(
|
||||
) (*playerPlayback, error) {
|
||||
videoBridge := playback.NewVideoBridge()
|
||||
statusStore := playback.NewStatusStore()
|
||||
mediaStats := playback.NewMediaStatsStore()
|
||||
audioSink := output.NewSDLAudioSink(audioDevice)
|
||||
videoSink := playback.VideoStatsSink{Stats: mediaStats, Sink: videoBridge}
|
||||
observedAudioSink := playback.AudioStatsSink{Stats: mediaStats, Sink: audioSink}
|
||||
|
||||
observe := func(status playback.Status) {
|
||||
statusStore.Observe(status)
|
||||
@@ -57,7 +61,7 @@ func newPlayerPlayback(
|
||||
|
||||
videoWorker, err := playback.NewVideoWorker(
|
||||
mxladapter.VideoFactory{},
|
||||
videoBridge,
|
||||
videoSink,
|
||||
retry,
|
||||
mxladapter.ShouldRetry,
|
||||
observe,
|
||||
@@ -74,7 +78,7 @@ func newPlayerPlayback(
|
||||
|
||||
audioWorker, err := playback.NewAudioWorker(
|
||||
mxladapter.AudioFactory{},
|
||||
audioSink,
|
||||
observedAudioSink,
|
||||
retry,
|
||||
mxladapter.ShouldRetry,
|
||||
observe,
|
||||
@@ -91,8 +95,8 @@ func newPlayerPlayback(
|
||||
|
||||
syncWorker, err := playback.NewSyncWorker(
|
||||
mxladapter.SyncFactory{},
|
||||
videoBridge,
|
||||
audioSink,
|
||||
videoSink,
|
||||
observedAudioSink,
|
||||
retry,
|
||||
mxladapter.ShouldRetry,
|
||||
observe,
|
||||
@@ -125,6 +129,7 @@ func newPlayerPlayback(
|
||||
Commands: make(chan playback.SessionCommand, 32),
|
||||
Video: videoBridge,
|
||||
Status: statusStore,
|
||||
MediaStats: mediaStats,
|
||||
Audio: audioSink,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user