commit 5b856b99dbd01a4ad0a83e2e9309ec4af9327875 Author: Javanaut Date: Sat Oct 21 09:30:51 2023 +0200 initial diff --git a/bin/ffx.py b/bin/ffx.py new file mode 100755 index 0000000..2f7bd90 --- /dev/null +++ b/bin/ffx.py @@ -0,0 +1,155 @@ +#! /usr/bin/python3 + +import sys, subprocess, json + +DEFAULT_QUALITY = 23 +DEFAULT_AV1_PRESET = 5 +DEFAULT_STEREO_BANDWIDTH = "128" +DEFAULT_AC3_BANDWIDTH = "256" +DEFAULT_DTS_BANDWIDTH = "320" + +def executeProcess(commandSequence): + + process = subprocess.Popen(commandSequence, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + output, error = process.communicate() + + return output + + +inputFilename = sys.argv[1] +outputFilename = sys.argv[2] + +targetFormat = 'vp9' +if 'av1' in sys.argv: + targetFormat = 'av1' +if 'vp9' in sys.argv: + targetFormat = 'vp9' + +quality = DEFAULT_QUALITY +qualityTokens = [q for q in sys.argv if q.startswith('q=')] +if qualityTokens: + quality = int(qualityTokens[0].split('=')[1]) + +preset = DEFAULT_AV1_PRESET +presetTokens = [p for p in sys.argv if p.startswith('p=')] +if presetTokens: + preset = int(presetTokens[0].split('=')[1]) + +stereoBandwidth = DEFAULT_STEREO_BANDWIDTH +stereoTokens = [s for s in sys.argv if s.startswith('a=')] +if stereoTokens: + stereoBandwidth = str(stereoTokens[0].split('=')[1]) +if not stereoBandwidth.endswith('k'): + stereoBandwidth += "k" + +ac3Bandwidth = DEFAULT_AC3_BANDWIDTH +ac3Tokens = [a for a in sys.argv if a.startswith('ac3=')] +if ac3Tokens: + ac3Bandwidth = str(ac3Tokens[0].split('=')[1]) +if not ac3Bandwidth.endswith('k'): + ac3Bandwidth += "k" + +dtsBandwidth = DEFAULT_DTS_BANDWIDTH +dtsTokens = [d for d in sys.argv if d.startswith('dts=')] +if dtsTokens: + dtsBandwidth = str(dtsTokens[0].split('=')[1]) +if not dtsBandwidth.endswith('k'): + dtsBandwidth += "k" + + +output = executeProcess(["ffprobe", "-show_streams", "-of", "json" ,inputFilename]) + +streamData = json.loads(output)['streams'] + +videoStreams = [s for s in streamData if s['codec_type'] == 'video'] +audioStreams = [s for s in streamData if s['codec_type'] == 'audio'] +subtitleStreams = [s for s in streamData if s['codec_type'] == 'subtitle'] + +for aStream in audioStreams: + print(f"audio stream: {aStream['channel_layout']}") + +commandTokens = ['ffmpeg', '-y', '-i', inputFilename] + + +mappingTokens = ['-map', 'v:0'] + +for a in range(len(audioStreams)): + mappingTokens += ['-map', f"a:{a}"] + +for s in range(len(subtitleStreams)): + mappingTokens += ['-map', f"s:{s}"] + + +audioTokens = [] +audioStreamIndex = 0 +for aStream in audioStreams: + channelLayout = aStream['channel_layout'] + + if channelLayout == '6.1': + audioTokens += [f"-c:a:{audioStreamIndex}", + 'libopus', + f"-filter:a:{audioStreamIndex}", + 'channelmap=channel_layout=6.1', + f"-b:a:{audioStreamIndex}", + dtsBandwidth] + + if channelLayout == '5.1(side)': + audioTokens += [f"-c:a:{audioStreamIndex}", + 'libopus', + f"-filter:a:{audioStreamIndex}", + "channelmap=FL-FL|FR-FR|FC-FC|LFE-LFE|SL-BL|SR-BR:5.1", + f"-b:a:{audioStreamIndex}", + ac3Bandwidth] + + if channelLayout == 'stereo': + audioTokens += [f"-c:a:{audioStreamIndex}", + 'libopus', + f"-b:a:{audioStreamIndex}", + stereoBandwidth] + + audioStreamIndex += 1 + + + +outputTokens = ['-f', 'webm', outputFilename] + +nullTokens = ['-f', 'null', '/dev/null'] + +av1Tokens = ['-c:v:0', + 'libsvtav1', + '-svtav1-params', + f"crf={quality}:preset={preset}:tune=0:enable-overlays=1:scd=1:scm=0", + '-pix_fmt', 'yuv420p10le'] + +vp9Pass1Tokens = ['-c:v:0', 'libvpx-vp9', '-row-mt', '1', + '-crf', str(quality), '-pass', '1', '-speed', '4', + '-frame-parallel', '0', '-g', '9999', '-aq-mode', '0'] + +vp9Pass2Tokens = ['-c:v:0', 'libvpx-vp9', '-row-mt', '1', + '-crf', str(quality), '-pass', '2', + '-frame-parallel', '0', '-g', '9999', '-aq-mode', '0', + '-auto-alt-ref', '1', '-lag-in-frames', '25'] + + +if targetFormat == 'av1': + + commandSequence = commandTokens + mappingTokens + av1Tokens + audioTokens + outputTokens + + print(f"Command Sequence: {commandSequence}") + + executeProcess(commandSequence) + +if targetFormat == 'vp9': + + commandSequence1 = commandTokens + vp9Pass1Tokens + audioTokens + nullTokens + + print(f"Command Sequence 1: {commandSequence1}") + + executeProcess(commandSequence1) + + commandSequence2 = commandTokens + mappingTokens + vp9Pass2Tokens + audioTokens + outputTokens + + print(f"Command Sequence 2: {commandSequence2}") + + executeProcess(commandSequence2)