Skip to content

Commit 83b1157

Browse files
Merge branch 'release/v1.8.0' into fix/stt-chunked-transcription
2 parents 1a53c52 + eb788be commit 83b1157

7 files changed

Lines changed: 622 additions & 96 deletions

File tree

crates/compositor/src/linux_decode.rs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use anyhow::{bail, Context, Result};
1616
use std::ffi::CString;
1717
use std::ptr;
1818

19+
use crate::timeline_walk::NextFrameTime;
20+
1921
use 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

Comments
 (0)