mirror of
https://github.com/master-of-zen/Av1an.git
synced 2024-11-25 02:29:40 +00:00
150 lines
5.1 KiB
Python
Executable file
150 lines
5.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
import concurrent
|
|
import concurrent.futures
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
import json
|
|
|
|
from utils import *
|
|
|
|
|
|
class Av1an:
|
|
"""Av1an - Python framework for AV1, VP9, VP8 encoding"""
|
|
def __init__(self):
|
|
self.args = arg_parsing()
|
|
|
|
def encoding_loop(self, commands):
|
|
"""Creating process pool for encoders, creating progress bar."""
|
|
try:
|
|
enc_path = self.args.temp / 'split'
|
|
done_path = self.args.temp / 'done.json'
|
|
|
|
if self.args.resume and done_path.exists():
|
|
log('Resuming...\n')
|
|
|
|
with open(done_path) as f:
|
|
data = json.load(f)
|
|
|
|
total = data['total']
|
|
done = len(data['done'])
|
|
initial = sum(data['done'].values())
|
|
|
|
log(f'Resumed with {done} encoded clips done\n\n')
|
|
else:
|
|
initial = 0
|
|
total = frame_probe_fast(self.args.input)
|
|
|
|
if total < 1:
|
|
total = frame_probe(self.args.input)
|
|
|
|
d = {'total': total, 'done': {}}
|
|
with open(done_path, 'w') as f:
|
|
json.dump(d, f)
|
|
|
|
clips = len([x for x in enc_path.iterdir() if x.suffix == ".mkv"])
|
|
self.args.workers = min(self.args.workers, clips)
|
|
|
|
print(f'\rQueue: {clips} Workers: {self.args.workers} Passes: {self.args.passes}\n'
|
|
f'Params: {self.args.video_params.strip()}')
|
|
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=self.args.workers) as executor:
|
|
counter = Manager().Counter(total, initial)
|
|
future_cmd = {executor.submit(encode, (cmd, counter, self.args)): cmd for cmd in commands}
|
|
for future in concurrent.futures.as_completed(future_cmd):
|
|
future_cmd[future]
|
|
try:
|
|
future.result()
|
|
except Exception as exc:
|
|
_, _, exc_tb = sys.exc_info()
|
|
print(f'Encoding error {exc}\nAt line {exc_tb.tb_lineno}')
|
|
terminate()
|
|
except KeyboardInterrupt:
|
|
terminate()
|
|
|
|
def video_encoding(self):
|
|
"""Encoding video on local machine."""
|
|
|
|
self.args.output_file = outputs_filenames(self.args.input, self.args.output_file, self.args.encoder )
|
|
|
|
if self.args.resume and (self.args.temp / 'done.json').exists():
|
|
set_log(self.args.logging, self.args.temp)
|
|
else:
|
|
setup(self.args.temp, self.args.resume)
|
|
set_log(self.args.logging, self.args.temp)
|
|
|
|
# inherit video params from aom encode unless we are using a different encoder, then use defaults
|
|
aom_keyframes_params = self.args.video_params if (self.args.encoder == 'aom') else AOM_KEYFRAMES_DEFAULT_PARAMS
|
|
framenums = split_routine(self.args, aom_keyframes_params)
|
|
|
|
if self.args.extra_split:
|
|
framenums = extra_splits(self.args.input, framenums, self.args.extra_split)
|
|
|
|
if self.args.reuse_first_pass:
|
|
segment_first_pass(self.args.temp, framenums)
|
|
|
|
segment(self.args.input, self.args.temp, framenums)
|
|
extract_audio(self.args.input, self.args.temp, self.args.audio_params)
|
|
|
|
chunk = get_video_queue(self.args.temp, self.args.resume)
|
|
|
|
# Make encode queue
|
|
commands = compose_encoding_queue(chunk, self.args.temp, self.args.encoder, self.args.video_params, self.args.ffmpeg_pipe, self.args.passes)
|
|
|
|
self.args.workers = determine_resources(self.args.encoder, self.args.workers)
|
|
|
|
self.encoding_loop(commands)
|
|
|
|
try:
|
|
concatenate_video(self.args.temp, self.args.output_file, self.args.encoder )
|
|
|
|
except Exception as e:
|
|
_, _, exc_tb = sys.exc_info()
|
|
print(f'Concatenation failed, FFmpeg error\nAt line: {exc_tb.tb_lineno}\nError:{str(e)}')
|
|
log(f'Concatenation failed, aborting, error: {e}\n')
|
|
terminate()
|
|
|
|
if self.args.vmaf or self.args.vmaf_plots:
|
|
plot_vmaf(self.args.input, self.args.output_file, model=self.args.vmaf_path)
|
|
# Delete temp folders
|
|
if not self.args.keep:
|
|
shutil.rmtree(self.args.temp)
|
|
|
|
def main_queue(self):
|
|
# Todo: Redo Queue
|
|
tm = time.time()
|
|
|
|
self.args.queue = process_inputs(self.args.input)
|
|
|
|
for file in self.args.queue:
|
|
tm = time.time()
|
|
self.args.input = file
|
|
|
|
if len(self.args.queue) > 1:
|
|
print(f'Encoding: {file}')
|
|
self.args.output_file = None
|
|
|
|
self.video_encoding()
|
|
print(f'Finished: {round(time.time() - tm, 1)}s\n')
|
|
|
|
def main_thread(self):
|
|
"""Main."""
|
|
startup_check()
|
|
conf(self.args)
|
|
check_executables(self.args.encoder)
|
|
self.main_queue()
|
|
|
|
|
|
def main():
|
|
try:
|
|
Av1an().main_thread()
|
|
except KeyboardInterrupt:
|
|
print('Encoding stopped')
|
|
sys.exit()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|