I wrote this for a 16 core 5950X, as ffmpeg's audio processing is single threaded. I haven't benchmarked if you get more performance using 32 queues (one per hardware thread rather than one per hardware core).
The main purpose is to have high-passed versions of a number of tracks or mixes, so that I can play them later through my hifi with the bass rolled off for the reason of being friendly to neighbours.
#!/usr/bin/env python
num_cores = 16
from glob import glob
from subprocess import run, PIPE, DEVNULL
from threading import Thread
import os, sys, re
from icecream import ic; ic.configureOutput(includeContext=True)
infiles = glob("*/*.m*") # all files are .m4a and .mp3 so this glob will do
outfiles = [ "../hp_mp3/"+x for x in infiles ]
zfiles = list(zip(infiles,outfiles))
# we have a list of lists, one per core
zfs = [[] for i in range(num_cores)]
# We assign the files to queues in a round-robin fashion
for i,x in enumerate(zfiles):
zfs[i%num_cores].append(x)
# do one file
def work(xs):
ifn,ofn = xs
# get bitrate of input file, so we can use the same bitrate on the encoded file
m = run(["ffprobe","-hide_banner",ifn],stdin=DEVNULL,stdout=PIPE,stderr=PIPE)
e = m.stderr.decode()
es = e.splitlines()
fs = [ x for x in es if "Audio:" in x ] # we'll get one line per audio stream
if len(fs) == 0:
ic("# fail",xs) # shouldn't happen
return
m = re.search(r"(\d+) kb/s",fs[0]) # hopefully this line includes a xxx kb/s bitrate
if not m:
ic("# fail2",xs) # shouldn't happen
return
br = m.group(1)
br = f"{br}k"
# do the transcoding
run(["ffmpeg","-i",ifn,"-b:a",br,"-af","highpass=f=200,highpass=f=200",ofn],stdin=DEVNULL)
# takes a list of (infile/outfile) pairs and processes them one after another
class Worker:
def __init__(self,zfs):
self.zfs = zfs
def __call__(self):
for xs in self.zfs:
work(xs)
threads = []
# create one thread per queue
for i,z in zfs.items():
thread = Thread(target=Worker(z))
threads.append(thread)
# start all the threads
for thread in threads:
thread.start()
# join all the threads
for thread in threads:
thread.join()