codec: cap FFmpeg decoder frame threads at 4

threads=auto spawns one decode thread per logical core per decoder (16
observed on a 24-core box); with cores-2 render workers decoding
concurrently the machine is oversubscribed several times over and the
playback-audio render thread gets starved (late chunks are dropped).
4 threads decode 1080p h264 far past real-time; preview throughput comes
from the worker pool, not per-decoder threading. Applies to both the
software open path and the hwaccel open path.
This commit is contained in:
2026-08-29 19:52:28 +08:00
parent 9dab9efc38
commit 81c3839937
2 changed files with 17 additions and 2 deletions
+16 -1
View File
@@ -559,6 +559,21 @@ struct AudioDecodeState {
carry_format: (u32, u64),
}
/// The frame-thread count handed to every FFmpeg decoder's `threads`
/// option. `auto` lets libavcodec spawn one decode thread per logical
/// core (16 observed on a 24-core box) PER DECODER; with ~`cores - 2`
/// render workers decoding concurrently that oversubscribes the machine
/// several times over and starves the playback-audio thread (late chunks
/// are dropped -> pops). 4 threads decode 1080p h264 far past real-time --
/// preview throughput comes from the worker pool, not from per-decoder
/// threading.
pub(crate) fn decoder_threads() -> String {
let cores = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4);
cores.min(4).to_string()
}
/// A swresample conversion context.
struct AudioResampler {
ctx: resampling::Context,
@@ -635,7 +650,7 @@ impl DecoderState {
let codec = ffmpeg::decoder::find(codec_id)
.ok_or_else(|| fail(format!("no decoder for codec {codec_id:?}")))?;
let mut open_opts = Dictionary::new();
open_opts.set("threads", "auto");
open_opts.set("threads", &decoder_threads());
ffmpeg::codec::Context::from_parameters(params)
.map_err(ffmpeg_err)?
.decoder()
+1 -1
View File
@@ -195,7 +195,7 @@ pub fn open_hw_accel(
// context frees it with the context.
unsafe { (*context.as_mut_ptr()).hw_device_ctx = device };
let mut opts = Dictionary::new();
opts.set("threads", "auto");
opts.set("threads", &crate::ffmpeg::decoder_threads());
context
.decoder()
.open_as_with(codec, opts)