Prime GStreamer frames after seeks

This commit is contained in:
Stu Leak 2026-01-10 03:05:07 -05:00
parent 15b09ee9df
commit 48fc94acb8

View File

@ -72,6 +72,7 @@ type GStreamerPlayer struct {
width int width int
height int height int
fps float64 fps float64
queued *image.RGBA
} }
func NewGStreamerPlayer(config Config) (*GStreamerPlayer, error) { func NewGStreamerPlayer(config Config) (*GStreamerPlayer, error) {
@ -248,14 +249,18 @@ func (p *GStreamerPlayer) SeekToTime(offset time.Duration) error {
} }
func (p *GStreamerPlayer) seekLocked(offset time.Duration) error { func (p *GStreamerPlayer) seekLocked(offset time.Duration) error {
return p.seekLockedWithFlags(offset, C.GST_SEEK_FLAG_FLUSH|C.GST_SEEK_FLAG_KEY_UNIT)
}
func (p *GStreamerPlayer) seekLockedWithFlags(offset time.Duration, flags C.GstSeekFlags) error {
if p.pipeline == nil { if p.pipeline == nil {
return errors.New("no pipeline loaded") return errors.New("no pipeline loaded")
} }
nanos := C.gint64(offset.Nanoseconds()) nanos := C.gint64(offset.Nanoseconds())
flags := C.GstSeekFlags(C.GST_SEEK_FLAG_FLUSH | C.GST_SEEK_FLAG_KEY_UNIT)
if C.gst_element_seek_simple(p.pipeline, C.GST_FORMAT_TIME, flags, nanos) == 0 { if C.gst_element_seek_simple(p.pipeline, C.GST_FORMAT_TIME, flags, nanos) == 0 {
return errors.New("gstreamer seek failed") return errors.New("gstreamer seek failed")
} }
p.primeAfterSeekLocked()
return nil return nil
} }
@ -266,7 +271,8 @@ func (p *GStreamerPlayer) SeekToFrame(frame int64) error {
return nil return nil
} }
seconds := float64(frame) / p.fps seconds := float64(frame) / p.fps
return p.seekLocked(time.Duration(seconds * float64(time.Second))) flags := C.GstSeekFlags(C.GST_SEEK_FLAG_FLUSH | C.GST_SEEK_FLAG_ACCURATE)
return p.seekLockedWithFlags(time.Duration(seconds*float64(time.Second)), flags)
} }
func (p *GStreamerPlayer) GetCurrentTime() time.Duration { func (p *GStreamerPlayer) GetCurrentTime() time.Duration {
@ -289,8 +295,19 @@ func (p *GStreamerPlayer) GetFrameImage() (*image.RGBA, error) {
if p.appsink == nil { if p.appsink == nil {
return nil, errors.New("gstreamer appsink unavailable") return nil, errors.New("gstreamer appsink unavailable")
} }
pullTimeout := C.GstClockTime(50 * 1000 * 1000) if p.queued != nil {
sample := C.gst_app_sink_try_pull_sample((*C.GstAppSink)(unsafe.Pointer(p.appsink)), pullTimeout) frame := p.queued
p.queued = nil
return frame, nil
}
return p.readFrameLocked(C.GstClockTime(50 * 1000 * 1000))
}
func (p *GStreamerPlayer) readFrameLocked(timeout C.GstClockTime) (*image.RGBA, error) {
if p.appsink == nil {
return nil, errors.New("gstreamer appsink unavailable")
}
sample := C.gst_app_sink_try_pull_sample((*C.GstAppSink)(unsafe.Pointer(p.appsink)), timeout)
if sample == nil { if sample == nil {
return nil, nil return nil, nil
} }
@ -345,6 +362,17 @@ func (p *GStreamerPlayer) GetFrameImage() (*image.RGBA, error) {
return img, nil return img, nil
} }
func (p *GStreamerPlayer) primeAfterSeekLocked() {
if p.appsink == nil {
return
}
frame, err := p.readFrameLocked(C.GstClockTime(200 * 1000 * 1000))
if err != nil || frame == nil {
return
}
p.queued = frame
}
func (p *GStreamerPlayer) SetVolume(level float64) error { func (p *GStreamerPlayer) SetVolume(level float64) error {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()