Av1an/main.py

146 lines
4.6 KiB
Python
Raw Normal View History

2020-01-08 00:39:49 +00:00
#!/usr/bin/python3
"""
mkvmerge required (python-pymkv)
2020-01-08 01:36:20 +00:00
ffmpeg required
TODO:
2020-01-08 22:43:20 +00:00
DONE make encoding queue with limiting by workers
DONE make concatenating videos after encoding
2020-01-09 14:54:01 +00:00
DONE make passing your arguments for encoding,
2020-01-08 01:52:57 +00:00
make separate audio and encode it separately,
2020-01-08 00:39:49 +00:00
"""
2020-01-11 01:18:01 +00:00
import sys
2020-01-08 00:20:18 +00:00
import os
from os.path import join
2020-01-09 19:35:43 +00:00
from psutil import virtual_memory
2020-01-10 23:18:36 +00:00
from subprocess import Popen, PIPE, call
2020-01-09 14:54:01 +00:00
import argparse
2020-01-09 15:50:26 +00:00
import time
2020-01-11 01:18:01 +00:00
from shutil import rmtree
from math import ceil
2020-01-08 20:44:44 +00:00
from multiprocessing import Pool
2020-01-08 18:56:47 +00:00
try:
import scenedetect
except:
print('ERROR: No PyScenedetect installed, try: sudo pip install scenedetect')
2020-01-08 00:20:18 +00:00
2020-01-11 01:18:11 +00:00
DEFAULT_ENCODE = ' --passes=1 --tile-columns=2 --tile-rows=2 --cpu-used=8 --end-usage=q --cq-level=63 --aq-mode=0'
2020-01-10 23:19:27 +00:00
FFMPEG = 'ffmpeg -hide_banner -loglevel warning '
2020-01-09 15:49:28 +00:00
def arg_parsing():
"""
Command line parser
2020-01-09 20:15:29 +00:00
Have default params
2020-01-09 15:49:28 +00:00
"""
parser = argparse.ArgumentParser()
2020-01-10 23:19:27 +00:00
parser.add_argument('--encoding_params', type=str, default=DEFAULT_ENCODE, help='AOMENC settings')
2020-01-09 15:49:28 +00:00
parser.add_argument('--input_file', '-i', type=str, default='bruh.mp4', help='input video file')
2020-01-09 20:15:29 +00:00
parser.add_argument('--num_worker', '-t', type=int, default=determine_resources(), help='number of encodes running at a time')
2020-01-09 15:49:28 +00:00
return parser.parse_args()
def determine_resources():
cpu = os.cpu_count()
2020-01-09 19:35:43 +00:00
ram = round(virtual_memory().total / 2**30)
return ceil(min(cpu, ram/2))
2020-01-08 00:20:18 +00:00
2020-01-08 22:43:20 +00:00
def extract_audio(input_vid):
2020-01-09 15:50:26 +00:00
"""
Extracting audio from video file
2020-01-09 20:15:29 +00:00
Encoding audio to opus.
Posible to -acodec copy to .mkv container without reencoding
2020-01-09 15:50:26 +00:00
"""
2020-01-10 23:18:36 +00:00
cmd = f'{FFMPEG} -i {join(os.getcwd(),input_vid)} -vn -acodec copy {join(os.getcwd(),"temp","audio.mkv")}'
Popen(cmd, shell=True).wait()
2020-01-08 22:43:20 +00:00
2020-01-08 00:39:49 +00:00
def split_video(input_vid):
2020-01-10 23:18:36 +00:00
cmd2 = f'scenedetect -q -i {input_vid} --output temp/split detect-content --threshold 50 split-video -c'
call(cmd2, shell=True)
2020-01-09 15:52:11 +00:00
print(f'Video {input_vid} splitted')
2020-01-08 00:20:18 +00:00
2020-01-08 18:56:47 +00:00
def get_video_queue(source_path):
videos = []
for root, dirs, files in os.walk(source_path):
for file in files:
f = os.path.getsize(os.path.join(root, file))
videos.append([file, f])
videos = sorted(videos, key=lambda x: -x[1])
2020-01-10 23:19:27 +00:00
print(f'Splited videos: {len(videos)}')
2020-01-08 18:56:47 +00:00
return videos
2020-01-08 20:44:44 +00:00
def encode(commands):
2020-01-09 15:33:47 +00:00
"""
Passing encoding params to ffmpeg for encoding
TODO:
Replace ffmpeg with aomenc because ffmpeg libaom doen't work with parameters properly
"""
2020-01-10 23:19:27 +00:00
cmd = f'{FFMPEG} {commands[0]}'
Popen(cmd, shell=True, stderr=PIPE).wait()
2020-01-10 23:48:26 +00:00
print(f'Done: {commands[1].split(".")[0].split("-")[-1]}')
2020-01-09 18:05:57 +00:00
2020-01-08 22:43:20 +00:00
2020-01-10 03:08:43 +00:00
def concat(input_video):
2020-01-08 22:43:20 +00:00
"""
Using FFMPEG to concatenate all encoded videos to 1 file.
Reading all files in A-Z order and saving it to concat.txt
"""
with open(f'{os.getcwd()}/temp/concat.txt', 'w') as f:
2020-01-08 20:44:44 +00:00
for root, firs, files in os.walk(join(os.getcwd(), 'temp', 'encode')):
2020-01-08 22:43:20 +00:00
for file in sorted(files):
f.write(f"file '{join(root, file)}'\n")
2020-01-08 20:44:44 +00:00
2020-01-11 01:18:33 +00:00
cmd = f'{FFMPEG} -f concat -safe 0 -i {join(os.getcwd(), "temp", "concat.txt")} -i {join(os.getcwd(), "temp", "audio.mkv")} -c copy -y {input_video.split(".")[0]}_av1.webm'
2020-01-10 23:18:36 +00:00
Popen(cmd, shell=True, stderr=PIPE).wait()
2020-01-08 21:27:21 +00:00
2020-01-09 14:54:01 +00:00
def main(input_video, encoding_params, num_worker):
2020-01-08 21:27:21 +00:00
2020-01-09 17:44:36 +00:00
# Make temporal directories, and remove them if already presented
if os.path.isdir(join(os.getcwd(), "temp")):
shutil.rmtree(join(os.getcwd(), "temp"))
2020-01-09 17:44:36 +00:00
os.makedirs(join(os.getcwd(), 'temp', 'split'))
os.makedirs(join(os.getcwd(), 'temp', 'encode'))
2020-01-08 21:27:21 +00:00
# Extracting audio
extract_audio(input_video)
2020-01-08 21:27:21 +00:00
# Spliting video and sorting big-first
2020-01-08 20:44:44 +00:00
split_video(input_video)
2020-01-10 23:19:27 +00:00
vid_queue = get_video_queue('temp/split')
2020-01-08 20:44:44 +00:00
files = [i[0] for i in vid_queue[:-1]]
2020-01-08 21:27:21 +00:00
# Making list of commands for encoding
2020-01-10 23:48:53 +00:00
commands = [(f'-i {join(os.getcwd(), "temp", "split", file)} -pix_fmt yuv420p -f yuv4mpegpipe - |' +
2020-01-10 23:49:32 +00:00
f' aomenc -q {encoding_params} -o {join(os.getcwd(), "temp", "encode", file)} -', file) for file in files]
2020-01-08 21:27:21 +00:00
# Creating threading pool to encode fixed amount of files at the same time
2020-01-10 23:48:26 +00:00
print(f'Starting encoding with {num_worker} workers. \nParameters:{encoding_params}\nEncoding..')
2020-01-09 14:54:01 +00:00
pool = Pool(num_worker)
2020-01-08 20:44:44 +00:00
pool.map(encode, commands)
2020-01-08 22:43:20 +00:00
# Merging all encoded videos to 1
2020-01-10 03:08:43 +00:00
concat(input_video)
2020-01-08 20:44:44 +00:00
2020-01-08 23:36:39 +00:00
2020-01-08 20:44:44 +00:00
if __name__ == '__main__':
2020-01-09 15:33:47 +00:00
2020-01-09 15:49:28 +00:00
args = arg_parsing()
2020-01-09 15:33:47 +00:00
# Main thread
2020-01-09 15:49:28 +00:00
start = time.time()
main(args.input_file, args.encoding_params, args.num_worker)
2020-01-10 23:19:27 +00:00
print(f'Completed in {round(time.time()-start, 1)} seconds')
2020-01-09 16:25:27 +00:00
# Delete temp folders
shutil.rmtree(join(os.getcwd(), "temp"))