remove redundant video grant protocol
This commit is contained in:
+28
-37
@@ -358,7 +358,7 @@ func main() {
|
||||
video string
|
||||
audio string
|
||||
}
|
||||
// One control channel: grant (empty params) or reconnect (with params).
|
||||
// Reconnect requests from GUI or automatic retry
|
||||
control := make(chan reconnectParams, 1)
|
||||
videoBridge := playback.NewVideoBridge()
|
||||
|
||||
@@ -377,7 +377,7 @@ func main() {
|
||||
audioSrc = nil
|
||||
}
|
||||
// Try once. Return error if fails — caller loops back to select
|
||||
// and can pick up new reconnect params or a new grant.
|
||||
// and can pick up newer reconnect request.
|
||||
if params.video != "" && params.audio != "" {
|
||||
s, e := source.OpenSameDomainSync(params.domain, params.video, params.audio)
|
||||
if e == nil {
|
||||
@@ -438,7 +438,7 @@ func main() {
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Audio-only mode: independent loop, no grant/staged handshake.
|
||||
// Audio-only mode: independent loop.
|
||||
if audioSrc != nil && syncSrc == nil && videoSrc == nil {
|
||||
for {
|
||||
select {
|
||||
@@ -503,31 +503,34 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Video (with or without sync) mode: grant/staged handshake.
|
||||
// Video bridge provides backpressure: only one borrowed frame is in flight.
|
||||
for {
|
||||
params := <-control
|
||||
if params.video != "" || params.audio != "" {
|
||||
// Reconnect request from Connect button or auto-retry.
|
||||
select {
|
||||
case <-control: // drain any pending grant
|
||||
default:
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
case params := <-control:
|
||||
if rerr := reopen(params); rerr != nil {
|
||||
if errors.Is(rerr, context.Canceled) {
|
||||
return
|
||||
}
|
||||
log.Printf("source: reopen failed: %v, retrying", rerr)
|
||||
|
||||
select {
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case control <- params:
|
||||
default:
|
||||
// Preserve an already queued, potentially newer request.
|
||||
}
|
||||
}
|
||||
continue
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
var videoFrame playback.VideoFrame
|
||||
@@ -538,15 +541,16 @@ func main() {
|
||||
return
|
||||
}
|
||||
log.Printf("source: %v", err)
|
||||
// Drain any pending grant, then send reconnect.
|
||||
select {
|
||||
case <-control:
|
||||
default:
|
||||
// Request a reconnect after the read failure.
|
||||
params := reconnectParams{
|
||||
domain: domainStr,
|
||||
video: videoStr,
|
||||
audio: audioStr,
|
||||
}
|
||||
select {
|
||||
case control <- reconnectParams{domain: domainStr, video: videoStr, audio: audioStr}:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case control <- params:
|
||||
default:
|
||||
// Preserve an already queued, potentially newer request.
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -570,14 +574,15 @@ func main() {
|
||||
return
|
||||
}
|
||||
log.Printf("source: %v", err)
|
||||
select {
|
||||
case <-control:
|
||||
default:
|
||||
params := reconnectParams{
|
||||
domain: domainStr,
|
||||
video: videoStr,
|
||||
audio: audioStr,
|
||||
}
|
||||
select {
|
||||
case control <- reconnectParams{domain: domainStr, video: videoStr, audio: audioStr}:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case control <- params:
|
||||
default:
|
||||
// Preserve an already queued, potentially newer request.
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -604,7 +609,6 @@ func main() {
|
||||
|
||||
running := true
|
||||
resized := false
|
||||
granted := false
|
||||
fullscreen := args.IsFullscreen
|
||||
if fullscreen {
|
||||
sdl.SetWindowFullscreen(windowHandler, true)
|
||||
@@ -662,15 +666,6 @@ func main() {
|
||||
}
|
||||
resized = false
|
||||
}
|
||||
if !granted {
|
||||
select {
|
||||
case control <- reconnectParams{}:
|
||||
granted = true
|
||||
case <-ctx.Done():
|
||||
running = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
var shownIndex uint64
|
||||
hasFrame := false
|
||||
|
||||
@@ -697,15 +692,11 @@ func main() {
|
||||
}
|
||||
|
||||
shownIndex = pendingFrame.Frame.Index
|
||||
granted = false
|
||||
hasFrame = true
|
||||
} else if frameErr != nil &&
|
||||
!errors.Is(frameErr, context.DeadlineExceeded) &&
|
||||
!errors.Is(frameErr, context.Canceled) {
|
||||
panic(frameErr)
|
||||
} else {
|
||||
// No frame arrived before the deadline.
|
||||
granted = false
|
||||
}
|
||||
|
||||
// stats
|
||||
|
||||
@@ -14,7 +14,7 @@ Size=200,200
|
||||
Collapsed=0
|
||||
|
||||
[Window][Connection]
|
||||
Pos=1250,701
|
||||
Pos=500,330
|
||||
Size=523,153
|
||||
Collapsed=0
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
That makes sense. The GUI is likely more responsive because frame staging is now serialized with rendering:
|
||||
- The background goroutine no longer copies a large frame into Vulkan-mapped memory concurrently with GUI/render work.
|
||||
- StageFrame waits for the GPU fence before writing, removing CPU/GPU memory contention and undefined synchronization.
|
||||
- The bridge creates deterministic backpressure: the source cannot begin another frame until the current payload is staged.
|
||||
- The main thread now controls the complete render sequence instead of coordinating loosely through two channels.
|
||||
So we fixed both correctness and scheduling stability without adding another frame copy.
|
||||
Reference in New Issue
Block a user