diff --git a/crates/compositor/src/pipeline_macos.rs b/crates/compositor/src/pipeline_macos.rs index 18ed75667..8fec0fb23 100644 --- a/crates/compositor/src/pipeline_macos.rs +++ b/crates/compositor/src/pipeline_macos.rs @@ -250,7 +250,13 @@ impl Decoder { // 2) La cible est DEVANT et à portée : dérouler depuis ici plutôt que de // repartir d'une image clé (cf. `pipeline_windows::SEEK_FORWARD_MAX_SEC`). if cur < seconds && seconds - cur <= SEEK_FORWARD_MAX_SEC { - return self.decode_forward_to(seconds, tb_sec); + let f = self.decode_forward_to(seconds, tb_sec)?; + if !f.is_null() { + return Ok(f); + } + // EOF atteint avant la cible (décodeur réactivé depuis le pool, laissé en fin + // de flux) : on retombe sur le seek keyframe complet au lieu de rendre `null` + // — qui forçait une réouverture complète. Voir `pipeline_windows::seek_to`. } } } diff --git a/crates/compositor/src/pipeline_windows.rs b/crates/compositor/src/pipeline_windows.rs index 1126d8e48..bbdab2642 100644 --- a/crates/compositor/src/pipeline_windows.rs +++ b/crates/compositor/src/pipeline_windows.rs @@ -619,7 +619,18 @@ impl Decoder { // repartir d'une image clé redevient moins cher — un seek coûte en moyenne // un demi-GOP, soit ~0,5 s sur nos captures. if cur < seconds && seconds - cur <= SEEK_FORWARD_MAX_SEC { - return self.decode_forward_to(seconds, tb_sec); + let f = self.decode_forward_to(seconds, tb_sec)?; + if !f.is_null() { + return Ok(f); + } + // `decode_forward_to` a atteint l'EOF avant la cible — typiquement un + // décodeur réactivé depuis le pool (`live::swap_clip_pooled`), laissé en fin + // de flux (`sent_eof`), qui ne peut plus avancer. On NE rend PAS `null` : ça + // forçait l'appelant à tout ROUVRIR (~190 ms mesurés), le pire à-coup ressenti + // au franchissement. On retombe sur le seek keyframe complet ci-dessous, qui + // rembobine + réarme le décodeur et repart proprement. Si la cible est + // réellement au-delà de l'EOF, ce seek complet rendra `null` lui aussi : + // comportement inchangé pour ce cas. } } } diff --git a/src/components/ai-edition/VirtualPreview.tsx b/src/components/ai-edition/VirtualPreview.tsx index 4476be836..0adc6da6e 100644 --- a/src/components/ai-edition/VirtualPreview.tsx +++ b/src/components/ai-edition/VirtualPreview.tsx @@ -619,10 +619,13 @@ export function VirtualPreview({ // handles clip-end advancement, so dropping the event // handler here is safe. /> - {loadState !== "ready" && ( -
- {loadState === "error" ? "Video preview could not be loaded." : "Loading preview…"} -
+ {/* Plus d'overlay « Loading preview… » : il reflétait l'état du