Skip to content

Commit a8cb4f6

Browse files
committed
fix(compositor): hold the webcam at its EOF instead of restarting it every tick
Review follow-up: a webcam track shorter than the screen — the camera stopping before the capture does, which the doc already called a normal case — put the player into permanent full-speed decoding. `target_webcam_t` is derived from the screen clock and keeps growing. At the webcam's EOF the catch-up loop seeked back to 0 and left the target untouched, so the NEXT tick restarted the catch-up from t=0 against a target still tens of minutes away and committed frames until the 1000-frame guard cut it off. Every tick. The webcam file was re-decoded end to end, forever, to display a track that had nothing left to show. Holding its last frame is both the fix and the semantics this PR is built on. Once EOF is a hold, the webcam decision is exactly the screen's at export time, so the loop now calls `frame_step` directly instead of carrying its own copy of the four cases — and `frame_step`'s tests cover this path too. 122/122 pass.
1 parent fbb3826 commit a8cb4f6

1 file changed

Lines changed: 19 additions & 19 deletions

File tree

crates/compositor/src/live.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::config::{self, Cfg};
2929
use crate::cursor::CursorTrack;
3030
use crate::d3d::Gpu;
3131
use crate::pipeline::Decoder;
32-
use crate::timeline_walk::NextFrameTime;
32+
use crate::timeline_walk::{frame_step, FrameStep, NextFrameTime};
3333
use anyhow::Result;
3434
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
3535
use std::sync::{Arc, Mutex};
@@ -370,9 +370,10 @@ impl Player {
370370
///
371371
/// La webcam suit le MÊME principe indépendamment (son propre temps source =
372372
/// `screen_time - webcam_offset_sec`, pas un pas 1:1 avec l'écran) : deux pipelines de
373-
/// capture indépendants n'ont pas la même cadence ni les mêmes trous. Elle boucle aussi
374-
/// de façon indépendante à son propre EOF (un clip webcam plus court que l'écran ne doit
375-
/// pas réinitialiser le décodeur écran).
373+
/// capture indépendants n'ont pas la même cadence ni les mêmes trous. Arrivée à son
374+
/// propre EOF — un clip webcam plus court que l'écran, cas normal quand la caméra
375+
/// s'arrête avant la capture — elle TIENT sa dernière image et laisse l'écran
376+
/// continuer seul, plutôt que de reboucler au début.
376377
pub unsafe fn step(&mut self, comp: &Compositor, cfg: &Cfg, target_source_time: f64) -> Result<bool> {
377378
let use_current = self.use_current_on_next_step;
378379
self.use_current_on_next_step = false;
@@ -416,27 +417,26 @@ impl Player {
416417
// même sémantique de hold que l'écran ci-dessus (et que `advance_decoder_to`) :
417418
// adopter une frame webcam dont le pts dépasse `target_webcam_t` l'afficherait
418419
// en avance sur son heure. Le garde-fou ne joue que contre un cas pathologique.
420+
//
421+
// BUG corrigé : à son EOF la webcam était reseekée à 0 alors que
422+
// `target_webcam_t` continue de croître avec le temps écran. Au tick
423+
// suivant, le rattrapage repartait donc de 0 et réavalait le fichier
424+
// entier vers une cible toujours aussi lointaine — 1000 frames par tick
425+
// (le plafond du garde-fou), en boucle, pour l'éternité. Un décodage
426+
// permanent à fond, pour afficher une webcam qui n'a plus rien à montrer.
427+
// Une fois l'EOF traité comme un hold, la décision webcam est EXACTEMENT
428+
// celle de l'écran à l'export : `frame_step` couvre les quatre cas sans
429+
// rien de spécifique, et ses tests couvrent donc aussi ce chemin.
419430
let mut wf = cur;
420431
let mut guard = 0u32;
421432
loop {
422-
match self.wdec.peek_next_time_sec()? {
423-
NextFrameTime::At(t) if t <= target_webcam_t => {
424-
wf = self.wdec.commit_peek()?
425-
}
426-
NextFrameTime::At(_) => break, // pas encore due : hold sur la courante.
427-
NextFrameTime::Unknown => {
428-
// pts webcam inexploitable : on avance d'UNE frame et on sort,
429-
// au lieu de vider la piste jusqu'à son EOF (ce que `0.0`
430-
// faisait, puisqu'il est toujours ≤ à la cible).
433+
match frame_step(self.wdec.peek_next_time_sec()?, 0.0, target_webcam_t) {
434+
FrameStep::Commit => wf = self.wdec.commit_peek()?,
435+
FrameStep::CommitAndStop => {
431436
wf = self.wdec.commit_peek()?;
432437
break;
433438
}
434-
NextFrameTime::Eof => {
435-
// Fin de la webcam avant l'écran : elle boucle SEULE — l'écran
436-
// garde sa propre position, inchangée.
437-
wf = self.wdec.seek_to(0.0)?;
438-
break;
439-
}
439+
FrameStep::Hold => break,
440440
}
441441
guard += 1;
442442
if guard > 1000 {

0 commit comments

Comments
 (0)