예제 #1
0
def get_video_queue(temp: Path, resume):
    """Returns sorted list of all videos that need to be encoded. Big first."""
    source_path = temp / 'split'
    queue = [x for x in source_path.iterdir() if x.suffix == '.mkv']

    done_file = temp / 'done.json'
    if resume and done_file.exists():
        try:
            with open(done_file) as f:
                data = json.load(f)
            data = data['done'].keys()
            queue = [x for x in queue if x.name not in data]
        except Exception as e:
            _, _, exc_tb = sys.exc_info()
            print(f'Error at resuming {e}\nAt line {exc_tb.tb_lineno}')

    queue = sorted(queue, key=lambda x: -x.stat().st_size)

    if len(queue) == 0:
        print(
            'Error: No files found in .temp/split, probably splitting not working'
        )
        terminate()

    return queue
예제 #2
0
def svt_av1_encode(inputs, passes, pipe, params):
    """SVT-AV1 encoding command composition."""
    encoder = 'SvtAv1EncApp'
    commands = []
    if not params:
        print('-w -h -fps is required parameters for svt_av1 encoder')
        terminate()

    if passes == 1:
        commands = [(
            f'-i {file[0]} {pipe} ' +
            f'  {encoder} -i stdin {params} -b {file[1].with_suffix(".ivf")} -',
            (file[0], file[1].with_suffix('.ivf'))) for file in inputs]

    if passes == 2:
        p2i = '-input-stat-file '
        p2o = '-output-stat-file '
        commands = [(
            f'-i {file[0]} {pipe} {encoder} -i stdin {params} {p2o} '
            f'{file[0].with_suffix(".stat")} -b {file[0]}.bk - ',
            f'-i {file[0]} {pipe} '
            f'{encoder} -i stdin {params} {p2i} {file[0].with_suffix(".stat")} -b '
            f'{file[1].with_suffix(".ivf")} - ', (file[0],
                                                  file[1].with_suffix('.ivf')))
                    for file in inputs]

    return commands
예제 #3
0
def get_video_queue(temp: Path, resume):
    """
    Compose and returns sorted list of all files that need to be encoded. Big first.

    :param temp: Path to temp folder
    :param resume: Flag on should already encoded chunks be discarded from queue
    :return: Sorted big first list of chunks to encode
    """
    source_path = temp / 'split'
    queue = [x for x in source_path.iterdir() if x.suffix == '.mkv']

    done_file = temp / 'done.json'
    if resume and done_file.exists():
        try:
            with open(done_file) as f:
                data = json.load(f)
            data = data['done'].keys()
            queue = [x for x in queue if x.name not in data]
        except Exception as e:
            _, _, exc_tb = sys.exc_info()
            print(f'Error at resuming {e}\nAt line {exc_tb.tb_lineno}')

    queue = sorted(queue, key=lambda x: -x.stat().st_size)

    if len(queue) == 0:
        er = 'Error: No files found in .temp/split, probably splitting not working'
        print(er)
        log(er)
        terminate()

    return queue
예제 #4
0
def call_vmaf(source: Path, encoded: Path, model=None, return_file=False):

    if model:
        mod = f":model_path={model}"
    else:
        mod = ''

    # For vmaf calculation both source and encoded segment scaled to 1080
    # for proper vmaf calculation
    fl = source.with_name(encoded.stem).with_suffix('.xml').as_posix()
    cmd = f'ffmpeg -loglevel error -hide_banner -r 60 -i {source.as_posix()} -r 60 -i {encoded.as_posix()}  ' \
          f'-filter_complex "[0:v]scale=1920:1080:flags=spline:force_original_aspect_ratio=decrease[scaled1];' \
          f'[1:v]scale=1920:1080:flags=spline:force_original_aspect_ratio=decrease[scaled2];' \
          f'[scaled2][scaled1]libvmaf=log_path={fl}{mod}" -f null - '

    c = subprocess.run(cmd, shell=True, stdout=PIPE, stderr=STDOUT)
    call = c.stdout
    # print(c.stdout.decode())
    if 'error' in call.decode().lower():
        print('\n\nERROR IN VMAF CALCULATION\n\n', call.decode())
        terminate()

    if return_file:
        return fl

    call = call.decode().strip()
    vmf = call.split()[-1]
    try:
        vmf = float(vmf)
    except ValueError:
        vmf = 0
    return vmf
예제 #5
0
def tqdm_bar(i, encoder, counter, frame_probe_source, passes):
    try:

        encoder_history = deque(maxlen=20)

        f, e = i.split('|')
        f = " ffmpeg -y -hide_banner -loglevel error " + f
        f, e = f.split(), e.split()
        frame = 0
        ffmpeg_pipe = subprocess.Popen(f, stdout=PIPE, stderr=STDOUT)
        pipe = subprocess.Popen(e,
                                stdin=ffmpeg_pipe.stdout,
                                stdout=PIPE,
                                stderr=STDOUT,
                                universal_newlines=True)

        while True:
            line = pipe.stdout.readline().strip()
            if line:
                encoder_history.append(line)
            if len(line) == 0 and pipe.poll() is not None:
                break

            if len(line) == 0:
                continue

            if encoder in ('aom', 'vpx', 'rav1e'):
                match = None
                if encoder in ('aom', 'vpx'):
                    if 'fatal' in line.lower():
                        print('\n\nERROR IN ENCODING PROCESS\n\n', line)
                        terminate()
                    if 'Pass 2/2' in line or 'Pass 1/1' in line:
                        match = re.search(r"frame.*?\/([^ ]+?) ", line)
                elif encoder == 'rav1e':
                    if 'error' in line.lower():
                        print('\n\nERROR IN ENCODING PROCESS\n\n', line)
                        terminate()
                    match = re.search(r"encoded.*? ([^ ]+?) ", 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)

        if pipe.returncode != 0 and pipe.returncode != -2:  # -2 is Ctrl+C for aom
            print(f"\nEncoder encountered an error: {pipe.returncode}")
            print('\n'.join(encoder_history))

    except Exception as e:
        _, _, exc_tb = sys.exc_info()
        print(f'Error at encode {e}\nAt line {exc_tb.tb_lineno}')
예제 #6
0
def compose_encoding_queue(files, temp, encoder, params, pipe, passes):
    """
    Composing encoding queue with split videos.
    :param files: List of files that need to be encoded
    :param temp: Path of temp folder
    :param encoder: Name of encoder to compose for
    :param params: Encoding parameters
    :param pipe: FFmpeg pipe
    :passes: Number of 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',
        'x265': 'x265'
    }
    enc_exe = encoders.get(encoder)
    inputs = [(temp / "split" / file.name, temp / "encode" / file.name, file)
              for file in files]

    if encoder in ('aom', 'vpx'):
        queue = aom_vpx_encode(inputs, enc_exe, passes, pipe, params)

    elif encoder == 'rav1e':
        queue = rav1e_encode(inputs, passes, pipe, params)

    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'
        log(er)
        terminate()

    log(f'Encoding Queue Composed\n'
        f'Encoder: {encoder.upper()} Queue Size: {len(queue)} Passes: {passes}\n'
        f'Params: {params}\n\n')

    return queue
예제 #7
0
def compose_encoding_queue(files, temp, encoder, params, pipe, passes):
    """Composing encoding queue with split videos."""
    encoders = {
        'svt_av1': 'SvtAv1EncApp',
        'rav1e': 'rav1e',
        'aom': 'aomenc',
        'vpx': 'vpxenc'
    }
    enc_exe = encoders.get(encoder)
    inputs = [(temp / "split" / file.name, temp / "encode" / file.name, file)
              for file in files]

    if encoder in ('aom', 'vpx'):
        if not params:
            if enc_exe == 'vpxenc':
                params = '--codec=vp9 --threads=4 --cpu-used=0 --end-usage=q --cq-level=30'

            if enc_exe == 'aomenc':
                params = '--threads=4 --cpu-used=6 --end-usage=q --cq-level=30'

        queue = aom_vpx_encode(inputs, enc_exe, passes, pipe, params)

    elif encoder == 'rav1e':
        if not params:
            params = ' --tiles 8 --speed 6 --quantizer 100'
        queue = rav1e_encode(inputs, passes, pipe, params)

    elif encoder == 'svt_av1':
        if not params:
            print('-w -h -fps is required parameters for svt_av1 encoder')
            terminate()
        queue = svt_av1_encode(inputs, passes, pipe, params)

    # Catch Error
    if len(queue) == 0:
        print('Error in making command queue')
        terminate()
    return queue, params