18

I need to reduce the framerate of some H.264 videos in the background without maxing out my CPU (on Linux). Somehow -threads 1 has no effect at all:

ffmpeg -threads 1 -i 50fps.mp4 -filter:v fps=30 30fps.mp4

Stream info:

Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(tv, bt470bg, progressive)
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D)

All the cores are still maxed out. How can I limit this to just one core? H.265 has an encoder-specific option for this (pools=none), is there anything similar I can use here?

1
  • 1
    For future readers, this example command line is super simplified, omitting -c:a copy so it also transcodes the audio (using FFmpeg's built-in AAC encoder that's generally lowish-quality last I read), losing quality and costing CPU time. And it uses the libx264 defaults of -preset medium -crf 23, so the quality per bitrate could be higher if you wanted to spend more CPU time, and you could spend more or less bitrate for more or less quality at similar encode speed. And some content benefits from -tune film. See trac.ffmpeg.org/wiki/Encode/H.264 May 18, 2022 at 1:04

6 Answers 6

28

You can do this with taskset:

taskset -c 0 ffmpeg ...

to run your ffmpeg process confined to the 1st CPU core (counting from 0). Or for example, to run it on the 1st and 3rd core:

taskset -c 0,2 ffmpeg ...
6
  • 5
    That works! 10+ years developing on Linux, and I didn't know this command. For the sake of completeness I'll wait for a "pure" ffmpeg answer before accepting this.
    – kontextify
    May 15, 2022 at 6:50
  • Interesting; libx264 somehow figures out (e.g. by checking its affinity mask) not to start the usual cores * 1.5 threads when you use taskset, so you don't have tons of context-switches with this. (And some per-thread memory which would have wasting memory bandwidth and L3 cache footprint.) May 15, 2022 at 16:04
  • So you don't need to use -threads:v 1 or -x264-params "threads=1" with this, it seems; libx264 infers that. (But even with threads=1, ffmpeg still runs video encoding in a separate thread from audio + other stuff, and the main ffmpeg thread, so you might not want to force it to compete with itself for a single core. With just threads=1, it uses about 108% of a single core, encoding 1080p at preset=medium crf23, and transcoding the audio instead of -c:a copy.) May 15, 2022 at 16:10
  • 9
    For the sake of anybody who actually ends up here searching for similar questions, this particular answer works, but it may still have unexpected effects on the performance of the rest of the system (CPU scheduling does not pin tasks to cores by default for very good reasons). May 15, 2022 at 16:35
  • @AustinHemmelgarn: So suppose I'm doing some heavy computations with multithreading and I want to pin a certain process with a certain CPU. Would that increase performance at all? I have a suspicion that my OS's (Mac or Linux) are shuffling my tasks around quite a bit, and I wonder if that hampers performance. Jun 15, 2022 at 23:11
15

Video encoding threads is set by output -threads.

So,

ffmpeg -i 50fps.mp4 -filter:v fps=30 -threads:v 1 30fps.mp4
10
  • 1
    This has the effect of reducing load but still distributing it to multiple cores. No single core is maxed out, but overall my 4 core CPU is running at ~50%. Maybe taskset is the only reliable way of doing this.
    – kontextify
    May 15, 2022 at 9:22
  • 4
    Yes, ffmpeg won't limit to specific cores. Also, output threads only limits encoding threads. There's decoding and filtering as well as the main program thread.
    – Gyan
    May 15, 2022 at 9:57
  • 11
    @kontextify that's normal and not bad. There's no benefit to keeping specific cores "available" versus letting the OS decide what's optimal at a given moment. If you
    – hobbs
    May 15, 2022 at 17:01
  • 4
    @hobbs - if you're trying to minimize interference with other processes, pinning to a single core can be a good idea. Bouncing between cores can thrash L1 caches, for one. It's not a free lunch - there are other downsides to it - but that's not the same as saying that there are no upsides.
    – TLW
    May 15, 2022 at 18:45
  • 8
    @TLW Linux is already aware of that and will try to reschedule a task on the same core it recently ran on, but it will break that rule for good reasons. cpuset pinning does have its uses (especially for network apps) but this isn't really one of them. In particular, unless you give every other process on the system a cpumask, it doesn't protect the cache as much as you would think. The times when ffmpeg is hopping cores without taskset are going to be pretty much the times when it wouldn't have helped to put it back on the same core anyway.
    – hobbs
    May 16, 2022 at 12:58
9

Is the purpose to prevent other software from working poorly because ffmpeg is hogging CPU in the background?

In that case I recommend running it with higher niceness instead:

nice -n 19 ffmpeg -i 50fps.mp4 -filter:v fps=30 30fps.mp4

This will allow the scheduler to minimize priority of ffmpeg and immediately preempt it in case any other process needs CPU, and still allows you to utilize all cores to encode as fast as possible.

5
  • 1
    It can make sense to limit number of threads to only the number of physical cores your CPU has, if it has 2 logical cores (SMT) for every physical. I've found with x265 that interactive use on GNU/Linux is affected much less by pools=4 on my 4c8t Skylake i7-6700k than by the default (starting at least as many threads as logical cores). Only gives up about 15% encode throughput. The benefit to other processes is from consuming less memory bandwidth and cache footprint, which nice doesn't help with; I've tried that in the past, and Chromium page loads and stuff were still noticeably slow. May 16, 2022 at 1:35
  • x264 defaults to threads = 1.5 * cores, so maybe threads = 6 on a 4c8t machine would be worth trying, or 5, or 4. Some tradeoff between max encode throughput at your chosen -preset medium -crf 23 or whatever vs. friendliness to other tasks. May 16, 2022 at 1:38
  • 1
    Nice is nice, but with all cores maxed out it still noticeably affects other CPU-intensive processes as @PeterCordes says. Scheduling, context switching, etc. There's always overhead there.
    – kontextify
    May 16, 2022 at 11:07
  • The issue might be I/O (especially if bottleneck is HDD seek time); ionice (in addition to nice) might help with that. May 16, 2022 at 15:34
  • @MatijaNalis: That's very unlikely when input and output are both using lossy compression (like in this case.) ffmpeg writing a .mp4 will by default encoding with libx264, which defaults to -preset medium -crf 23. And transcodes the audio, too, to AAC, because the OP didn't specify -c:a copy. On a quad-core Skylake i7-6700k at 3.9GHz, for example, it encodes about 53 fps on some random 1080p video, the output file write averaged 651 Kbytes/second, over 3 minutes. The input file read bandwidth was only a couple times that. I/O could be an issue if reading lossless png or Y4M. May 16, 2022 at 16:17
2

ffmpeg-only solution without nice/taskset/cpulimit/cgroups/docker:

ffmpeg -threads 1 -i 50fps.mp4 -filter:v fps=30 -threads 1 30fps.mp4

The position of -threads 1 (and its count) does matter.

ffmpeg command has the following format:

ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ... 

In your command with -threads 1 before -i a single thread will be used for decoding of 50fps.mp4 but also you need to specify the number of threads for video encoding (after -i):

ffmpeg -threads 1 -i 50fps.mp4 -filter:v fps=30 -threads 1 30fps.mp4

Also you may need to check out -filter_threads or -filter_complex_threads in case of filter_complex graphs.

1
  • Unintuitive, but this does work! For me 1 decoding + 1 encoding thread basically uses a single core. With 2 encoding threads the load is 60-70% evenly on all 4 cores which I want to avoid. Taskset seems a better solution overall.
    – kontextify
    Aug 24 at 2:08
1

If you are using a Linux distribution which uses systemd, then you can use one if its utilities named systemd-run for resource control. For your case, it would be this:

sudo systemd-run --scope -p AllowedCPUs=VALUE CMD
sudo systemd-run --scope -p CPUQuota=VALUE% CMD 

For AllowedCPUs, you can mention the indice of the logical core to which you want to restrict the execution of your command. Indice is 0 for first logical core, 1 for second logical core, and so on.

For CPUQuota, 100% equals one logical core's maximum available CPU time. 200% would be two logical cores, and so on.

Example A:

# My stress commands wants to use all 8 logical cores of my machine, 
# but systemd would limit it to allowed logical cores 0 and 4. 
# I chose 0 and 4 because both belongs to the same physical core.

sudo systemd-run --scope -p AllowedCPUs=0,4 stress -c 8

Example B:

# Here, my stress command despite being hungry for all CPUs would only
get 100% *available* time of one logical core only.

sudo systemd-run --scope CPUQuota=100% stress -c 8

Suggested readings: manual of systemd-run and systemd.resource-control.

1

I routinely use cpulimit to throttle the overall burden on my machine with nightly LARGE compressed backups, such as the following:

{ sleep 1 ; tar -zcf output.tar.gz /mnt/3TBNumber3C/HUGE_dir ; killall cpulimit ; } & cpulimit -e /bin/gzip -l 30

I wondered what the difference was between cpulimit and taskset and I gather from this site that cpulimit is a throttle whereas taskset establishes affinities. Indeed, if I am up late enough I can actually hear my cooling fans going up and down so that the overall average load on the CPU cores is 30 percent (in the above example).

I have also used this to throttle ffmpeg for the same reason as the questioner asked, though I am usually doing that "hands on".

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .