Av1an/av1an/manager/Manager.py

161 lines
5.1 KiB
Python
Raw Normal View History

import json
import shutil
2020-12-05 22:24:15 +00:00
import sys
import time
2020-12-27 00:18:42 +00:00
from multiprocessing.managers import BaseManager
2020-12-05 22:24:15 +00:00
from pathlib import Path
from typing import List
from av1an.chunk import Chunk
from av1an.chunk.chunk_queue import load_or_gen_chunk_queue
from av1an.ffmpeg import extract_audio
from av1an.fp_reuse import segment_first_pass
from av1an.logger import log, set_log
from av1an.project.Project import Project
from av1an.split import split_routine
from av1an.startup.file_validation import process_inputs
2021-01-06 04:09:02 +00:00
2021-01-03 11:29:13 +00:00
from av1an.utils import frame_probe, terminate
from av1an.vmaf import VMAF
2020-12-27 04:58:18 +00:00
from .Counter import BaseManager, Counter, Manager
2021-01-06 04:09:02 +00:00
from .Queue import Queue
2020-12-05 22:24:15 +00:00
class Main:
def __init__(self, args):
self.file_queue: list[Path] = []
self.args = args
self.file_queue = process_inputs(args.input)
self.projects = self.create_project_list()
def create_project_list(self):
"""
Returns list of initialized Project objects with single input
"""
queue = []
for file in self.file_queue:
project = Project(vars(self.args))
project.input = file
project.outputs_filenames()
2021-05-10 04:46:02 +00:00
project.promt_output_overwrite()
2020-12-05 22:24:15 +00:00
queue.append(project)
return queue
def run(self):
"""
Run encoding in queue or single file
"""
for i, proj in enumerate(self.projects):
if proj.output_file.exists() and len(self.projects) > 1:
2021-02-01 03:07:07 +00:00
print(
f":: Skipping file {proj.input.name}\n:: Outputfile {proj.output_file.name} exists"
)
2020-12-05 22:24:15 +00:00
2020-12-11 10:09:33 +00:00
# Don't print new line on last project to console
2021-02-01 03:07:07 +00:00
if i + 1 < len(self.projects):
2020-12-05 22:24:15 +00:00
print()
continue
try:
tm = time.time()
if len(self.projects) > 1:
print(f":: Encoding file {proj.input.name}")
EncodingManager().encode_file(proj)
2020-12-05 22:24:15 +00:00
2021-04-12 23:23:48 +00:00
print(f"Finished: {round(time.time() - tm, 1)}s\n")
2020-12-05 22:24:15 +00:00
except KeyboardInterrupt:
2021-04-12 23:23:48 +00:00
print("Encoding stopped")
2020-12-05 22:24:15 +00:00
sys.exit()
class EncodingManager:
def __init__(self):
self.workers = None
2020-12-27 00:18:42 +00:00
self.vmaf = None
2021-01-06 04:09:02 +00:00
self.initial_frames = 0
2020-12-05 22:24:15 +00:00
def encode_file(self, project: Project):
"""
Encodes a single video file on the local machine.
:param project: The project for this encode
:return: None
"""
project.setup()
set_log(project.logging, project.temp)
# find split locations
split_locations = split_routine(project, project.resume)
# create a chunk queue
2021-04-12 23:23:48 +00:00
chunk_queue = load_or_gen_chunk_queue(project, project.resume, split_locations)
2021-01-06 04:09:02 +00:00
self.done_file(project, chunk_queue)
if not project.resume:
extract_audio(project.input, project.temp, project.audio_params)
if project.reuse_first_pass:
segment_first_pass(project.temp, split_locations)
# do encoding loop
project.determine_workers()
self.startup(project, chunk_queue)
2021-01-06 04:09:02 +00:00
queue = Queue(project, chunk_queue)
queue.encoding_loop()
2021-04-12 23:23:48 +00:00
if queue.status.lower() == "fatal":
msg = "FATAL Encoding process encountered fatal error, shutting down"
print("\n::", msg)
log(msg)
sys.exit(1)
# concat
project.concat_routine()
if project.vmaf or project.vmaf_plots:
2021-04-12 23:23:48 +00:00
self.vmaf = VMAF(
n_threads=project.n_threads,
model=project.vmaf_path,
res=project.vmaf_res,
vmaf_filter=project.vmaf_filter,
)
2020-12-27 00:18:42 +00:00
self.vmaf.plot_vmaf(project.input, project.output_file, project)
# Delete temp folders
if not project.keep:
shutil.rmtree(project.temp)
2021-01-06 04:09:02 +00:00
def done_file(self, project: Project, chunk_queue: List[Chunk]):
2021-04-12 23:23:48 +00:00
done_path = project.temp / "done.json"
if project.resume and done_path.exists():
2021-04-12 23:23:48 +00:00
log("Resuming...")
with open(done_path) as done_file:
data = json.load(done_file)
2021-04-12 23:23:48 +00:00
project.set_frames(data["frames"])
done = len(data["done"])
self.initial_frames = sum(data["done"].values())
log(f"Resumed with {done} encoded clips done")
else:
2021-01-06 04:09:02 +00:00
self.initial_frames = 0
total = project.get_frames()
2021-04-12 23:23:48 +00:00
d = {"frames": total, "done": {}}
with open(done_path, "w") as done_file:
json.dump(d, done_file)
2021-01-06 04:09:02 +00:00
def startup(self, project: Project, chunk_queue: List[Chunk]):
clips = len(chunk_queue)
project.workers = min(project.workers, clips)
2021-02-01 03:07:07 +00:00
print(
2021-04-12 23:23:48 +00:00
f"\rQueue: {clips} Workers: {project.workers} Passes: {project.passes}\n"
f'Params: {" ".join(project.video_params)}'
)
BaseManager.register("Counter", Counter)
counter = Manager().Counter(
project.get_frames(), self.initial_frames, not project.quiet
)
project.counter = counter