@@ -16,6 +16,8 @@ use anyhow::{bail, Context, Result};
1616use std:: ffi:: CString ;
1717use std:: ptr;
1818
19+ use crate :: timeline_walk:: NextFrameTime ;
20+
1921use crate :: ffi:: {
2022 av_frame_alloc, av_frame_free, av_frame_move_ref, av_frame_unref, av_packet_alloc,
2123 av_packet_free, av_packet_unref, av_read_frame, av_seek_frame, avcodec_alloc_context3,
@@ -58,6 +60,11 @@ pub struct SwDecoder {
5860 frame : * mut AVFrame ,
5961 sent_eof : bool ,
6062 cur_pts : Option < i64 > ,
63+ /// Buffer de lookahead pour `peek_next_time_sec` : symétrique de
64+ /// `pipeline_macos::Decoder::peek_frame` — cf. là-bas pour la justification.
65+ peek_frame : * mut AVFrame ,
66+ /// `true` si `peek_frame` porte une frame décodée en attente de `commit_peek`.
67+ has_peek : bool ,
6168}
6269
6370/// Libère toutes les ressources ffmpeg. `Drop` ne peut pas faillir ; on
@@ -78,6 +85,9 @@ impl Drop for SwDecoder {
7885 if !self . pkt . is_null ( ) {
7986 av_packet_free ( & mut self . pkt ) ;
8087 }
88+ if !self . peek_frame . is_null ( ) {
89+ av_frame_free ( & mut self . peek_frame ) ;
90+ }
8191 }
8292 }
8393}
@@ -177,7 +187,8 @@ impl SwDecoder {
177187 } ;
178188 let pkt = av_packet_alloc ( ) ;
179189 let frame = av_frame_alloc ( ) ;
180- if pkt. is_null ( ) || frame. is_null ( ) {
190+ let peek_frame = av_frame_alloc ( ) ;
191+ if pkt. is_null ( ) || frame. is_null ( ) || peek_frame. is_null ( ) {
181192 avcodec_free_context ( & mut dec) ;
182193 avformat_close_input ( & mut fmt) ;
183194 bail ! ( "av_packet_alloc/av_frame_alloc (pompage sequentiel)" ) ;
@@ -192,6 +203,8 @@ impl SwDecoder {
192203 frame,
193204 sent_eof : false ,
194205 cur_pts : None ,
206+ peek_frame,
207+ has_peek : false ,
195208 } )
196209 }
197210
@@ -201,21 +214,33 @@ impl SwDecoder {
201214 /// seek PAS : le decodeur garde son etat, donc une lecture sequentielle coute
202215 /// UN packet par frame au lieu d'un re-parcours de demi-GOP.
203216 pub unsafe fn next_frame ( & mut self ) -> Result < * mut AVFrame > {
217+ if self . has_peek {
218+ return self . commit_peek ( ) ;
219+ }
220+ if !self . receive_into ( self . frame ) ? {
221+ return Ok ( ptr:: null_mut ( ) ) ;
222+ }
223+ let pts = ( * self . frame ) . best_effort_timestamp ;
224+ self . cur_pts = if pts == i64:: MIN { None } else { Some ( pts) } ;
225+ Ok ( self . frame )
226+ }
227+
228+ /// Décode dans `into` (buffer courant ou de lookahead) jusqu'à obtenir une frame ou
229+ /// l'EOF — cf. `pipeline_macos::Decoder::receive_into` pour la justification.
230+ unsafe fn receive_into ( & mut self , into : * mut AVFrame ) -> Result < bool > {
204231 loop {
205- let r = avcodec_receive_frame ( self . dec , self . frame ) ;
232+ let r = avcodec_receive_frame ( self . dec , into ) ;
206233 if r == 0 {
207- let pts = ( * self . frame ) . best_effort_timestamp ;
208- self . cur_pts = if pts == i64:: MIN { None } else { Some ( pts) } ;
209- return Ok ( self . frame ) ;
234+ return Ok ( true ) ;
210235 }
211236 if r == AVERROR_EOF {
212- return Ok ( ptr :: null_mut ( ) ) ;
237+ return Ok ( false ) ;
213238 }
214239 if r != AVERROR_EAGAIN {
215240 bail ! ( "avcodec_receive_frame: {r}" ) ;
216241 }
217242 if self . sent_eof {
218- return Ok ( ptr :: null_mut ( ) ) ;
243+ return Ok ( false ) ;
219244 }
220245 let rr = av_read_frame ( self . fmt , self . pkt ) ;
221246 if rr < 0 {
@@ -240,6 +265,41 @@ impl SwDecoder {
240265 }
241266 }
242267
268+ /// Décode la prochaine frame dans le buffer de lookahead et renvoie son temps.
269+ /// Cf. `pipeline_macos::Decoder::peek_next_time_sec`.
270+ pub ( crate ) unsafe fn peek_next_time_sec ( & mut self ) -> Result < NextFrameTime > {
271+ if !self . has_peek {
272+ if !self . receive_into ( self . peek_frame ) ? {
273+ return Ok ( NextFrameTime :: Eof ) ;
274+ }
275+ self . has_peek = true ;
276+ }
277+ let pts = ( * self . peek_frame ) . best_effort_timestamp ;
278+ // Sans pts ni time_base exploitables on ne PEUT pas dire si la frame est due :
279+ // `Unknown`, et non `0.0` — qui passait pour « due » à tous les coups.
280+ Ok ( if pts == i64:: MIN || self . stream_timebase <= 0.0 {
281+ NextFrameTime :: Unknown
282+ } else {
283+ NextFrameTime :: At ( pts as f64 * self . stream_timebase )
284+ } )
285+ }
286+
287+ /// Promeut la frame de lookahead au rang de frame courante. Cf.
288+ /// `pipeline_macos::Decoder::commit_peek`.
289+ pub ( crate ) unsafe fn commit_peek ( & mut self ) -> Result < * mut AVFrame > {
290+ // `bail!` et non `debug_assert!` : compilée en release, l'assertion disparaissait
291+ // et l'échange promouvait un `AVFrame` jamais rempli, avec un
292+ // `best_effort_timestamp` indéterminé, jusque dans le chemin de présentation.
293+ if !self . has_peek {
294+ bail ! ( "commit_peek sans peek_next_time_sec préalable" ) ;
295+ }
296+ std:: mem:: swap ( & mut self . frame , & mut self . peek_frame ) ;
297+ self . has_peek = false ;
298+ let pts = ( * self . frame ) . best_effort_timestamp ;
299+ self . cur_pts = if pts == i64:: MIN { None } else { Some ( pts) } ;
300+ Ok ( self . frame )
301+ }
302+
243303 /// Temps source (secondes) de la derniere frame rendue par `next_frame` /
244304 /// `decode_at`, tire du pts REEL et non d'un compteur d'index.
245305 pub fn cur_time_sec ( & self ) -> Option < f64 > {
@@ -263,6 +323,8 @@ impl SwDecoder {
263323 /// `AVERROR_INVALIDDATA` plutôt que de paniquer : la prochaine itération
264324 /// lira le packet complet suivant.
265325 pub unsafe fn decode_at ( & mut self , frame_idx : u32 ) -> Result < * mut AVFrame > {
326+ // Tout seek invalide un éventuel peek en attente — cf. pipeline_macos::Decoder::seek_to.
327+ self . has_peek = false ;
266328 let fps = self . fps ;
267329 let target_ts = ( frame_idx as f64 / fps) * 1_000_000.0 ; // AV_TIME_BASE = µs
268330 // `AVSEEK_FLAG_BACKWARD` vaut 1, pas 4 — 4 est `AVSEEK_FLAG_ANY`. La constante
0 commit comments