package playback import ( "context" "sync" ) type PendingVideoFrame struct { Frame VideoFrame Generation uint64 Source FeedConfig completeOnce sync.Once result chan error } type VideoBridge struct { requests chan *PendingVideoFrame } func NewVideoBridge() *VideoBridge { return &VideoBridge{ requests: make(chan *PendingVideoFrame), } } func (b *VideoBridge) ConsumeVideo( ctx context.Context, frame VideoFrame, ) error { pending := &PendingVideoFrame{ Frame: frame, Generation: generationFromContext(ctx), Source: videoSourceFromContext(ctx), result: make(chan error, 1), } select { case b.requests <- pending: case <-ctx.Done(): return ctx.Err() } // The render thread now owns temporary access to the borrowed payload. // We must wait for Complete even if ctx is canceled. return <-pending.result } func (b *VideoBridge) Next( ctx context.Context, ) (*PendingVideoFrame, error) { select { case pending := <-b.requests: return pending, nil case <-ctx.Done(): return nil, ctx.Err() } } // TryNext returns a frame only when a producer is already waiting. It never // blocks the caller, allowing UI/render loops to run independently of video // frame cadence. A returned frame has the same completion requirements as // one returned by Next. func (b *VideoBridge) TryNext() (*PendingVideoFrame, bool) { select { case pending := <-b.requests: return pending, true default: return nil, false } } func (f *PendingVideoFrame) Complete(err error) { f.completeOnce.Do(func() { f.result <- err }) }