fix(portal): convert PipeWire nanosecond PTS to encoder frame-number units
PipeWire spa_meta_header.pts is CLOCK_MONOTONIC in nanoseconds, but the encoder expects frame-number units (time_base = 1/fps). The raw nanosecond value was assigned directly to AVFrame.pts, causing the encoder/muxer to interpret timestamps as billions of frames, producing corrupted duration metadata and broken rate control. Fix: record the first frame's PTS as a nanosecond base, compute elapsed nanoseconds for each subsequent frame, then convert to frame numbers via elapsed_ns * fps / 1_000_000_000. Using elapsed time avoids i64 overflow on absolute timestamps (~10^18 ns). Matches the WLR path pattern (state.rs:525-527) which converts microseconds to frame numbers for the same encoder.
This commit is contained in:
@@ -25,6 +25,7 @@ pub struct StatePortal {
|
||||
errored: bool,
|
||||
first_frame: bool,
|
||||
drm_device: PathBuf,
|
||||
first_pts_ns: Option<i64>,
|
||||
}
|
||||
|
||||
impl StatePortal {
|
||||
@@ -43,6 +44,7 @@ impl StatePortal {
|
||||
errored: false,
|
||||
first_frame: true,
|
||||
drm_device,
|
||||
first_pts_ns: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,9 +176,16 @@ impl StatePortal {
|
||||
bail!("av_hwframe_transfer_data failed: error {ret}");
|
||||
}
|
||||
|
||||
// 7. Set PTS
|
||||
// 7. Set PTS — convert PipeWire nanoseconds to encoder frame-number units
|
||||
// PipeWire PTS is CLOCK_MONOTONIC in nanoseconds.
|
||||
// Encoder time_base = 1/fps, so PTS must be in frame numbers.
|
||||
// Use elapsed time since first frame to avoid i64 overflow on absolute timestamps.
|
||||
let fps_i64 = self.args.fps as i64;
|
||||
let base_ns = *self.first_pts_ns.get_or_insert(frame.pts.max(0));
|
||||
let elapsed_ns = (frame.pts.max(0) - base_ns).max(0);
|
||||
let pts = elapsed_ns * fps_i64 / 1_000_000_000;
|
||||
unsafe {
|
||||
(*hw_frame.as_mut_ptr()).pts = frame.pts;
|
||||
(*hw_frame.as_mut_ptr()).pts = pts;
|
||||
}
|
||||
|
||||
// 8. Encode
|
||||
|
||||
Reference in New Issue
Block a user