diff --git a/internal/player/gstreamer_player.go b/internal/player/gstreamer_player.go index 586a0e3..3899d96 100644 --- a/internal/player/gstreamer_player.go +++ b/internal/player/gstreamer_player.go @@ -153,7 +153,20 @@ func (p *GStreamerPlayer) Load(path string, offset time.Duration) error { p.appsink = appsink p.paused = true + // Set to PAUSED to preroll (loads first frame) C.gst_element_set_state(playbin, C.GST_STATE_PAUSED) + + // Wait for preroll to complete (first frame ready) + bus := C.gst_element_get_bus(playbin) + if bus != nil { + defer C.gst_object_unref(C.gpointer(bus)) + // Wait up to 5 seconds for preroll + msg := C.gst_bus_timed_pop_filtered(bus, 5000000000, C.GST_MESSAGE_ASYNC_DONE|C.GST_MESSAGE_ERROR) + if msg != nil { + C.gst_message_unref(msg) + } + } + if offset > 0 { _ = p.seekLocked(offset) } diff --git a/main.go b/main.go index ac1f2c7..f2e05ee 100644 --- a/main.go +++ b/main.go @@ -11247,9 +11247,14 @@ func newPlaySession(path string, w, h int, fps, duration float64, targetW, targe return nil } + logging.Info(logging.CatPlayer, "GStreamer loaded video: %s (%.2f fps, %dx%d)", path, fps, targetW, targetH) + // Start frame display loop go sess.frameDisplayLoop() + // Pause initially (GStreamer is already paused after Load) + sess.paused = true + return sess } @@ -11356,12 +11361,12 @@ func (p *playSession) frameDisplayLoop() { defer ticker.Stop() frameCount := 0 - logging.Debug(logging.CatPlayer, "playSession: frameDisplayLoop started (fps=%.2f)", p.fps) + logging.Info(logging.CatPlayer, "playSession: frameDisplayLoop started (fps=%.2f, interval=%v)", p.fps, frameDuration) for { select { case <-p.stop: - logging.Debug(logging.CatPlayer, "playSession: frameDisplayLoop stopped") + logging.Info(logging.CatPlayer, "playSession: frameDisplayLoop stopped") return case <-ticker.C: @@ -11369,12 +11374,7 @@ func (p *playSession) frameDisplayLoop() { continue } - // Skip frame updates when paused - if p.paused { - continue - } - - // Get current frame from GStreamer + // Get current frame from GStreamer (even when paused, for seeking) frame, err := p.gstPlayer.GetFrameImage() if err != nil { logging.Debug(logging.CatPlayer, "Frame read error: %v", err) @@ -11382,6 +11382,7 @@ func (p *playSession) frameDisplayLoop() { } if frame == nil { + // No frame available yet - pipeline may be buffering continue } @@ -11391,6 +11392,7 @@ func (p *playSession) frameDisplayLoop() { p.frameN = frameCount currentTime := p.gstPlayer.GetCurrentTime() p.current = currentTime.Seconds() + isPaused := p.paused p.mu.Unlock() // Update UI on main thread @@ -11398,8 +11400,10 @@ func (p *playSession) frameDisplayLoop() { if p.img != nil { p.img.Image = frame p.img.Refresh() + logging.Debug(logging.CatPlayer, "Frame %d updated (%.2fs, paused=%v, size=%dx%d)", + frameCount, p.current, isPaused, frame.Bounds().Dx(), frame.Bounds().Dy()) } - if p.prog != nil { + if p.prog != nil && !isPaused { p.prog(p.current) } if p.frameFunc != nil {