Added x265 support

This commit is contained in:
Zen 2020-07-14 10:25:10 +03:00
parent dbce1ab3aa
commit f7a9eb4fde
5 changed files with 75 additions and 25 deletions

View file

@ -131,7 +131,7 @@ class Av1an:
v = call_vmaf(i[1], i[2], model=self.vmaf_path, return_file=True)
# Trying 25 percentile
mean = read_vmaf_xml(v , 25)
mean = read_vmaf_xml(v, 25)
vmaf_cq.append((mean, i[3]))
@ -268,7 +268,8 @@ class Av1an:
def video_encoding(self):
"""Encoding video on local machine."""
self.output_file = outputs_filenames(self.input, self.output_file)
self.output_file = outputs_filenames(self.input, self.output_file, self.encoder )
if self.resume and (self.temp / 'done.json').exists():
set_log(self.logging, self.temp)

View file

@ -29,7 +29,7 @@ def arg_parsing():
parser.add_argument('--passes', '-p', type=int, default=2, help='Specify encoding passes', choices=[1, 2])
parser.add_argument('--video_params', '-v', type=str, default=None, help='encoding settings')
parser.add_argument('--encoder', '-enc', type=str, default='aom', help='Choosing encoder',
choices=['aom', 'svt_av1', 'rav1e', 'vpx'])
choices=['aom', 'svt_av1', 'rav1e', 'vpx','x265'])
parser.add_argument('--workers', '-w', type=int, default=0, help='Number of workers')
parser.add_argument('-cfg', '--config', type=Path, help='Parameters file. Save/Read: '
'Video, Audio, Encoder, FFmpeg parameteres')

View file

@ -48,7 +48,8 @@ def tqdm_bar(i, encoder, counter, frame_probe_source, passes):
pipe = subprocess.Popen(e, stdin=ffmpeg_pipe.stdout, stdout=PIPE,
stderr=STDOUT,
universal_newlines=True)
pass_1_check = True
skip_1_pass = False
while True:
line = pipe.stdout.readline().strip()
if line:
@ -58,9 +59,9 @@ def tqdm_bar(i, encoder, counter, frame_probe_source, passes):
if len(line) == 0:
continue
if encoder in ('aom', 'vpx', 'rav1e'):
if encoder in ('aom', 'vpx', 'rav1e','x265'):
match = None
if encoder in ('aom', 'vpx'):
if 'fatal' in line.lower():
print('\n\nERROR IN ENCODING PROCESS\n\n', line)
@ -73,12 +74,23 @@ def tqdm_bar(i, encoder, counter, frame_probe_source, passes):
terminate()
match = re.search(r"encoded.*? ([^ ]+?) ", line)
elif encoder in ('x265'):
if not skip_1_pass and pass_1_check:
if 'output file' in line:
if 'nul' in line.lower():
skip_1_pass = True
else:
pass_1_check = False
if not skip_1_pass:
match = re.search(r"^(\d+)", line)
if match:
new = int(match.group(1))
if new > frame:
counter.update(new - frame)
frame = new
if encoder == 'svt_av1':
counter.update(frame_probe_source // passes)

View file

@ -43,13 +43,10 @@ def get_default_params_for_encoder(enc):
DEFAULT_ENC_PARAMS = {
'vpx': '--codec=vp9 --threads=4 --cpu-used=0 --end-usage=q --cq-level=30',
'aom': '--threads=4 --cpu-used=6 --end-usage=q --cq-level=30',
'rav1e': ' --tiles 8 --speed 6 --quantizer 100'
# SVT-AV1 requires params for -w -h -fps
'rav1e': ' --tiles 8 --speed 6 --quantizer 100',
'svt_av1': ' --preset 4 --rc 0 --qp 25 ',
'x265': ' -p slow --crf 23 --vbv-maxrate 10000 --vbv-bufsize 8000 ',
}
# TODO(n9Mtq4): we can get the width, height, and fps of the video and generate default params for svt
if enc == 'svt_av1':
print('-w -h -fps is required parameters (--video_params) for svt_av1 encoder')
terminate()
return DEFAULT_ENC_PARAMS[enc]
@ -66,9 +63,6 @@ def svt_av1_encode(inputs, passes, pipe, params):
"""
encoder = 'SvtAv1EncApp'
commands = []
if not params:
print('-w -h -fps is required parameters for svt_av1 encoder')
terminate()
if passes == 1:
commands = [
@ -124,7 +118,8 @@ def aom_vpx_encode(inputs, enc, passes, pipe, params):
def rav1e_encode(inputs, passes, pipe, params):
"""
Generates commands for AOM, VPX encoders
Generates commands for Rav1e encoder
Currently 2 pass rav1e piping doesn't work
:param inputs: Files that need to be enocoded
:param passes: Encoding passes
@ -153,7 +148,9 @@ def rav1e_encode(inputs, passes, pipe, params):
f' rav1e - --first-pass {file[0].with_suffix(".stat")} {params} '
f'--output {file[1].with_suffix(".ivf")}',
f'-i {file[0]} {pipe} '
f' rav1e - --second-pass {file[0].with_suffix(".stat")} {params} '
f' rav1e - --second-pass {file[0].with_suffix(".stat")} {pa65 [info]: Residual QT: max TU size, max depth : 32 / 1 inter / 1 intra
265
rams} '
f'--output {file[1].with_suffix(".ivf")}',
(file[0], file[1].with_suffix('.ivf')))
for file in inputs]
@ -161,6 +158,38 @@ def rav1e_encode(inputs, passes, pipe, params):
return commands
def x265_encode(inputs, passes, pipe, params):
"""
Generates commands for AOM, VPX encoders
:param inputs: Files that need to be enocoded
:param passes: Encoding passes
:param pipe: FFmpeg piping settings
:param params: Encoding parameters
:return: Composed commands for execution
"""
commands = []
single_p = 'x265 --y4m'
two_p_1 = 'x265 --pass 1 --y4m'
two_p_2 = 'x265 --pass 2 --y4m'
if passes == 1:
commands = [
(f' -i {file[0]} {pipe} {single_p} {params} - -o {file[1].with_suffix(".ivf")}',
(file[0], file[1].with_suffix('.ivf')))
for file in inputs
]
if passes == 2:
commands = [
(f' -i {file[0]} {pipe} {two_p_1} {params} --stats {file[0].with_suffix(".log")} - -o {os.devnull}',
f' -i {file[0]} {pipe} {two_p_2} {params} --stats {file[0].with_suffix(".log")} - -o {file[1].with_suffix(".ivf")}',
(file[0], file[1].with_suffix('.ivf')))
for file in inputs
]
return commands
def compose_encoding_queue(files, temp, encoder, params, pipe, passes):
"""
Composing encoding queue with split videos.
@ -174,7 +203,7 @@ def compose_encoding_queue(files, temp, encoder, params, pipe, passes):
assert params is not None # params needs to be set with at least get_default_params_for_encoder before this func
encoders = {'svt_av1': 'SvtAv1EncApp', 'rav1e': 'rav1e', 'aom': 'aomenc', 'vpx': 'vpxenc'}
encoders = {'svt_av1': 'SvtAv1EncApp', 'rav1e': 'rav1e', 'aom': 'aomenc', 'vpx': 'vpxenc', 'x265': 'x265'}
enc_exe = encoders.get(encoder)
inputs = [(temp / "split" / file.name,
temp / "encode" / file.name,
@ -189,6 +218,9 @@ def compose_encoding_queue(files, temp, encoder, params, pipe, passes):
elif encoder == 'svt_av1':
queue = svt_av1_encode(inputs, passes, pipe, params)
elif encoder == 'x265':
queue = x265_encode(inputs, passes, pipe, params)
# Catch Error
if len(queue) == 0:
er = 'Error in making command queue'

View file

@ -23,7 +23,7 @@ def determine_resources(encoder, workers):
if encoder in ('aom', 'rav1e', 'vpx'):
workers = round(min(cpu / 2, ram / 1.5))
elif encoder == 'svt_av1':
elif encoder in ('svt_av1', 'x265'):
workers = round(min(cpu, ram)) // 5
# fix if workers round up to 0
@ -34,7 +34,7 @@ def determine_resources(encoder, workers):
def check_executables(encoder):
encoders = {'svt_av1': 'SvtAv1EncApp', 'rav1e': 'rav1e', 'aom': 'aomenc', 'vpx': 'vpxenc'}
encoders = {'svt_av1': 'SvtAv1EncApp', 'rav1e': 'rav1e', 'aom': 'aomenc', 'vpx': 'vpxenc','x265': 'x265'}
if not find_executable('ffmpeg'):
print('No ffmpeg')
terminate()
@ -47,7 +47,7 @@ def check_executables(encoder):
print(f'Encoder {enc} not found')
terminate()
else:
print(f'Not valid encoder {encoder}\nValid encoders: "aom rav1e", "svt_av1", "vpx" ')
print(f'Not valid encoder {encoder}\nValid encoders: "aom rav1e", "svt_av1", "vpx", "x265" ')
terminate()
@ -62,8 +62,13 @@ def setup(temp: Path, resume):
(temp / 'encode').mkdir(exist_ok=True)
def outputs_filenames(inp: Path, out:Path):
if out:
return out.with_suffix('.mkv')
def outputs_filenames(inp: Path, out:Path, encoder):
if encoder == 'x265':
suffix = '.mp4'
else:
return Path(f'{inp.stem}_av1.mkv')
suffix = '.mkv'
if out:
return out.with_suffix(suffix)
else:
return Path(f'{inp.stem}_av1{suffix}')