fix(qsv): end the inner borrow before the retarget param rebuild

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 19:39:08 +02:00
parent 55e7f3fca9
commit 75d1322b8a
+27 -21
View File
@@ -1401,30 +1401,36 @@ impl Encoder for QsvEncoder {
/// requires completed sync operations, so the in-flight window is drained (into `ready`) /// requires completed sync operations, so the in-flight window is drained (into `ready`)
/// first; a drain that can't finish inside the budget falls back to the caller's rebuild. /// first; a drain that can't finish inside the budget falls back to the caller's rebuild.
fn reconfigure_bitrate(&mut self, bps: u64) -> bool { fn reconfigure_bitrate(&mut self, bps: u64) -> bool {
let Some(inner) = self.inner.as_mut() else { // Drain phase in its own scope so the `inner` borrow ends before the param rebuild
self.bitrate_bps = bps; // below reads `self` again (the raw session pointer stays valid — `self.inner` is not
return true; // touched in between).
}; let session = {
let deadline = std::time::Instant::now() + BUSY_BUDGET; let Some(inner) = self.inner.as_mut() else {
while !inner.pending.is_empty() { self.bitrate_bps = bps;
match sync_one(inner, 5) { return true;
Ok(Some(au)) => inner.ready.push_back(au), };
Ok(None) => { let deadline = std::time::Instant::now() + BUSY_BUDGET;
if std::time::Instant::now() >= deadline { while !inner.pending.is_empty() {
tracing::warn!( match sync_one(inner, 5) {
"QSV bitrate retarget: in-flight frames didn't settle — falling \ Ok(Some(au)) => inner.ready.push_back(au),
back to a rebuild" Ok(None) => {
); if std::time::Instant::now() >= deadline {
tracing::warn!(
"QSV bitrate retarget: in-flight frames didn't settle — falling \
back to a rebuild"
);
return false;
}
std::thread::sleep(std::time::Duration::from_micros(250));
}
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "QSV retarget drain failed");
return false; return false;
} }
std::thread::sleep(std::time::Duration::from_micros(250));
}
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "QSV retarget drain failed");
return false;
} }
} }
} inner.session.0
};
let old = self.bitrate_bps; let old = self.bitrate_bps;
self.bitrate_bps = bps; self.bitrate_bps = bps;
let cfg = self.encode_config(); let cfg = self.encode_config();
@@ -1441,7 +1447,7 @@ impl Encoder for QsvEncoder {
set.par.NumExtParam = set.ptrs.len() as u16; set.par.NumExtParam = set.ptrs.len() as u16;
// SAFETY: session live on this thread, no operation in flight (drained above); the // SAFETY: session live on this thread, no operation in flight (drained above); the
// param block + ext chain outlive the synchronous call. // param block + ext chain outlive the synchronous call.
let sts = unsafe { vpl::MFXVideoENCODE_Reset(inner.session.0, &mut set.par) }; let sts = unsafe { vpl::MFXVideoENCODE_Reset(session, &mut set.par) };
if sts < vpl::MFX_ERR_NONE { if sts < vpl::MFX_ERR_NONE {
tracing::warn!( tracing::warn!(
mbps = bps / 1_000_000, mbps = bps / 1_000_000,