#!/usr/bin/env python
## Copyright (c) 2021, Alliance for Open Media. All rights reserved
##
## This source code is subject to the terms of the BSD 3-Clause Clear License and the
## Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License was
## not distributed with this source code in the LICENSE file, you can obtain it
## at aomedia.org/license/software-license/bsd-3-c-c/.  If the Alliance for Open Media Patent
## License 1.0 was not distributed with this source code in the PATENTS file, you
## can obtain it at aomedia.org/license/patent-license/.
##
__author__ = "maggie.sun@intel.com, ryanlei@meta.com"

import math
import os

import Utils
from Config import (
    AOMENC,
    AV1ENC,
    CTC_VERSION,
    EnableECF,
    EnableOpenGOP,
    EnableTemporalFilter,
    EnableTimingInfo,
    EnableVerificationTestConfig,
    GOP_SIZE,
    HEVCCfgFile,
    HMENC,
    Platform,
    SUB_GOP_SIZE,
    SVTAV1,
    UsePerfUtil,
)
from Utils import ConvertY4MToYUV, DeleteFile, ExecuteCmd, GetShortContentName


def EncodeWithAOM_AV2(
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    args = (
        " --verbose --codec=av2 -v --psnr --obu --frame-parallel=0"
        " --cpu-used=%s --limit=%d --skip=%d --passes=1 --end-usage=q --i%s "
        " --use-fixed-qp-offsets=1 --deltaq-mode=0 "
        " --enable-tpl-model=0 --fps=%d/%d -w %d -h %d"
        % (
            preset,
            framenum,
            start_frame,
            clip.fmt,
            clip.fps_num,
            clip.fps_denom,
            clip.width,
            clip.height,
        )
    )

    # config encoding bitdepth
    if EnableECF and CTC_VERSION in ["8.0", "9.0"]:
        # ECF: always encode at 10-bit (Section 6.2)
        args += " --input-bit-depth=%d --bit-depth=10" % (clip.bit_depth)
    elif (CTC_VERSION in ["6.0", "7.0", "8.0", "9.0"]) and (
        clip.file_class in ["A2", "A4", "B1"]
    ):
        # CWG-D088
        args += " --input-bit-depth=%d --bit-depth=10" % (clip.bit_depth)
    else:
        args += " --input-bit-depth=%d --bit-depth=%d" % (
            clip.bit_depth,
            clip.bit_depth,
        )

    if CTC_VERSION in ["2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0"]:
        args += " --qp=%d" % QP
    else:
        args += " --use-16bit-internal --cq-level=%d" % QP

    # Tile and threading configuration
    if EnableECF and CTC_VERSION in ["8.0", "9.0"]:
        # ECF tiling rules (Section 6.2)
        if test_cfg == "RA":
            if clip.width >= 3840 and clip.height >= 2160:
                # 4K+: 4 col tiles x 4 row tiles, 16 threads
                args += " --tile-rows=2 --tile-columns=2 --threads=16 --row-mt=0 "
            elif clip.width >= 1920 and clip.height >= 858:
                # FHD+: 2 col tiles x 2 row tiles, 4 threads
                args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
            else:
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "LD":
            if clip.width >= 1920 and clip.height >= 858:
                # FHD+: 4 col tiles x 2 row tiles, 8 threads
                args += " --tile-rows=1 --tile-columns=2 --threads=8 --row-mt=0 "
            elif clip.width == 1280 and clip.height == 720:
                # 720p: 2 col tiles, 2 threads
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "AI":
            if clip.width >= 3840 and clip.height >= 2160:
                # 4K+: 2 col tiles, 2 threads
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        else:
            args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
    elif (
        (CTC_VERSION in ["4.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --threads=2 --row-mt=0 "
    elif (
        (CTC_VERSION in ["5.0", "6.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif (
        (CTC_VERSION in ["6.0"])
        and (clip.file_class in ["E", "G1"])
        and (test_cfg == "RA")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif CTC_VERSION in ["7.0", "8.0", "9.0"]:
        if EnableVerificationTestConfig:
            args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "RA":
            if clip.file_class in ["A1", "E", "G1"]:
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.file_class in ["A2", "B1"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "AS":
            # use the same configuration as RA
            if (clip.width == 3840 and clip.height == 2160) or (
                clip.width == 2560 and clip.height == 1440
            ):
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.width == 1920 and clip.height == 1080:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "LD":
            if clip.file_class in ["A2", "B1"]:
                # 8 tiles should be used
                args += " --tile-rows=1 --tile-columns=2 --threads=8 --row-mt=0 "
            elif clip.file_class in ["A3"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif (test_cfg in ["AI", "STILL"]) and (
            clip.width >= 3840 and clip.height >= 2160
        ):
            # 2 tiles should be used
            args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    elif clip.width >= 3840 and clip.height >= 2160:
        args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    else:
        args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "

    if EnableOpenGOP:
        args += " --enable-fwd-kf=1 "
    else:
        args += " --enable-fwd-kf=0 "

    if EnableTemporalFilter:
        args += " --enable-keyframe-filtering=1 "
    else:
        args += " --enable-keyframe-filtering=0 "

    # Content-specific flags
    if EnableECF and CTC_VERSION in ["8.0", "9.0"] and clip.file_class == "ECF-6":
        # ECF-6: screen content coding (Section 6.3)
        args += " --tune-content=screen --enable-intrabc-ext=1 --enable-extended-sdp=0"
    elif CTC_VERSION in ["8.0", "9.0"]:
        # CWG-D082, CWG-F222
        if clip.file_class in ["B2"]:
            args += " --tune-content=screen --enable-intrabc-ext=1"
        elif clip.file_class in ["A4", "A5"]:
            args += " --enable-intrabc-ext=2"
        else:
            args += " --enable-intrabc-ext=1"
    elif CTC_VERSION in ["6.0", "7.0"]:
        if clip.file_class in ["B2"]:
            args += " --tune-content=screen --enable-intrabc-ext=1"
        else:
            args += " --enable-intrabc-ext=2"

    if test_cfg == "AI" or test_cfg == "STILL":
        args += " --kf-min-dist=0 --kf-max-dist=0 "
    elif test_cfg == "RA" or test_cfg == "AS":
        args += (
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d"
            " --gf-max-pyr-height=%d --kf-min-dist=%d --kf-max-dist=%d"
            " --lag-in-frames=%d --auto-alt-ref=1 "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
                GOP_SIZE,
                GOP_SIZE,
                SUB_GOP_SIZE + 3,
            )
        )
    elif test_cfg == "LD":
        args += (
            " --kf-min-dist=9999 --kf-max-dist=9999 --lag-in-frames=0"
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d "
            " --gf-max-pyr-height=%d --subgop-config-str=ld "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
            )
        )
    else:
        print("Unsupported Test Configuration %s" % test_cfg)

    # HDR encoding flags
    if EnableECF and CTC_VERSION in ["8.0", "9.0"] and clip.file_class in ["ECF-3", "ECF-4"]:
        # ECF HDR classes (Section 6.1.3, 6.1.4)
        args += " --color-primaries=bt2020 --transfer-characteristics=smpte2084 --matrix-coefficients=bt2020ncl"
        args += " --chroma-sample-position=topleft "
    elif clip.file_class == "G1" or clip.file_class == "G2":
        args += " --color-primaries=bt2020 --transfer-characteristics=smpte2084 --matrix-coefficients=bt2020ncl"
        if CTC_VERSION in ["8.0", "9.0"]:
            args += " --chroma-sample-position=topleft "
        else:
            args += " --chroma-sample-position=colocated "

    args += " -o %s %s" % (outfile, clip.file_path)
    cmd = AOMENC + args + "> %s 2>&1" % enc_log
    if EnableTimingInfo:
        if Platform == "Windows":
            cmd = "ptime " + cmd + " >%s" % enc_perf
        elif Platform == "Darwin":
            cmd = "gtime --verbose --output=%s " % enc_perf + cmd
        else:
            if UsePerfUtil:
                cmd = "3>%s perf stat --log-fd 3 " % enc_perf + cmd
            else:
                cmd = "/usr/bin/time --verbose --output=%s " % enc_perf + cmd
    ExecuteCmd(cmd, LogCmdOnly)


# encode with libaom to achieve highest coding gain
def EncodeWithAOM_AV1_Unconstrained(
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    args = (
        " --verbose --codec=av1 --profile=0 -v --psnr --obu --frame-parallel=0"
        " --cpu-used=%s --limit=%d --skip=%d --passes=1 --end-usage=q --min-q=0 --max-q=63 --i%s "
        "  --cq-level=%d --enable-tpl-model=1 --fps=%d/%d -w %d -h %d"
        % (
            preset,
            framenum,
            start_frame,
            clip.fmt,
            QP,
            clip.fps_num,
            clip.fps_denom,
            clip.width,
            clip.height,
        )
    )

    # config enoding bitdepth
    if (CTC_VERSION in ["6.0", "7.0", "8.0", "9.0"]) and (
        clip.file_class in ["A2", "A4", "B1"]
    ):
        # CWG-D088
        args += " --input-bit-depth=%d --bit-depth=10" % (clip.bit_depth)
    else:
        args += " --input-bit-depth=%d --bit-depth=%d" % (
            clip.bit_depth,
            clip.bit_depth,
        )

    # For 4K clip, encode with 2 tile columns using two threads.
    # --tile-columns value is in log2.
    if (
        (CTC_VERSION in ["4.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --threads=2 --row-mt=0 "
    elif (
        (CTC_VERSION in ["5.0", "6.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif (
        (CTC_VERSION in ["6.0"])
        and (clip.file_class in ["E", "G1"])
        and (test_cfg == "RA")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif CTC_VERSION in ["7.0", "8.0", "9.0"]:
        if EnableVerificationTestConfig:
            args += " --tile-rows=0 --tile-columns=0 --threads=0 --row-mt=0  "
            args += " --drop-frame=0 --static-thresh=0 --minsection-pct=0 --maxsection-pct=200 --arnr-maxframes=7 --arnr-strength=5 --sharpness=0 "
        elif test_cfg == "RA":
            if clip.file_class in ["A1", "E", "G1"]:
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.file_class in ["A2", "B1"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "AS":
            # use the same configuration as RA
            if (clip.width == 3840 and clip.height == 2160) or (
                clip.width == 2560 and clip.height == 1440
            ):
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.width == 1920 and clip.height == 1080:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "LD":
            if clip.file_class in ["A2", "B1"]:
                # 8 tiles should be used
                args += " --tile-rows=1 --tile-columns=2 --threads=8 --row-mt=0 "
            elif clip.file_class in ["A3"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif (test_cfg in ["AI", "STILL"]) and (
            clip.width >= 3840 and clip.height >= 2160
        ):
            # 2 tiles should be used
            args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    elif clip.width >= 3840 and clip.height >= 2160:
        args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    else:
        args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "

    if EnableOpenGOP:
        args += " --enable-fwd-kf=1 "
    else:
        args += " --enable-fwd-kf=0 "

    if EnableTemporalFilter:
        args += " --enable-keyframe-filtering=1 "
    else:
        args += " --enable-keyframe-filtering=0 "

    if test_cfg == "AI" or test_cfg == "STILL":
        args += " --kf-min-dist=0 --kf-max-dist=0 "
    elif test_cfg == "RA" or test_cfg == "AS":
        args += (
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d"
            " --gf-max-pyr-height=%d --kf-min-dist=%d --kf-max-dist=%d"
            " --lag-in-frames=35 --auto-alt-ref=1 --passes=2 --undershoot-pct=100 --overshoot-pct=100 "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
                GOP_SIZE,
                GOP_SIZE,
            )
        )
    elif test_cfg == "LD":
        args += (
            " --kf-min-dist=9999 --kf-max-dist=9999 --lag-in-frames=0"
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d "
            " --gf-max-pyr-height=%d --auto-alt-ref=1 --passes=1 --undershoot-pct=25 --overshoot-pct=25 "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
            )
        )
    else:
        print("Unsupported Test Configuration %s" % test_cfg)

    if clip.file_class == "G1" or clip.file_class == "G2":
        args += (
            "--color-primaries=bt2020 --transfer-characteristics=smpte2084 "
            "--matrix-coefficients=bt2020ncl --chroma-sample-position=colocated "
        )

    args += " -o %s %s" % (outfile, clip.file_path)
    cmd = AV1ENC + args + "> %s 2>&1" % enc_log
    if EnableTimingInfo:
        if Platform == "Windows":
            cmd = "ptime " + cmd + " >%s" % enc_perf
        elif Platform == "Darwin":
            cmd = "gtime --verbose --output=%s " % enc_perf + cmd
        else:
            if UsePerfUtil:
                cmd = "3>%s perf stat --log-fd 3 " % enc_perf + cmd
            else:
                cmd = "/usr/bin/time --verbose --output=%s " % enc_perf + cmd
    ExecuteCmd(cmd, LogCmdOnly)


# encode with libaom to compliant with CTC configuration
def EncodeWithAOM_AV1_Constrained(
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    args = (
        " --verbose --codec=av1 -v --psnr --obu --frame-parallel=0"
        " --cpu-used=%s --limit=%d --skip=%d --passes=1 --end-usage=q --i%s "
        " --use-fixed-qp-offsets=1 --deltaq-mode=0 --cq-level=%d"
        " --enable-tpl-model=0 --fps=%d/%d -w %d -h %d"
        % (
            preset,
            framenum,
            start_frame,
            clip.fmt,
            QP,
            clip.fps_num,
            clip.fps_denom,
            clip.width,
            clip.height,
        )
    )

    # config enoding bitdepth
    if (CTC_VERSION in ["6.0", "7.0", "8.0", "9.0"]) and (
        clip.file_class in ["A2", "A4", "B1"]
    ):
        # CWG-D088
        args += " --input-bit-depth=%d --bit-depth=10" % (clip.bit_depth)
    else:
        args += " --input-bit-depth=%d --bit-depth=%d" % (
            clip.bit_depth,
            clip.bit_depth,
        )

    # For 4K clip, encode with 2 tile columns using two threads.
    # --tile-columns value is in log2.
    if (
        (CTC_VERSION in ["4.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --threads=2 --row-mt=0 "
    elif (
        (CTC_VERSION in ["5.0", "6.0"])
        and (clip.file_class in ["A2", "B1"])
        and (test_cfg == "LD")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif (
        (CTC_VERSION in ["6.0"])
        and (clip.file_class in ["E", "G1"])
        and (test_cfg == "RA")
    ):
        args += " --tile-rows=1 --tile-columns=1 --threads=4 --row-mt=0 "
    elif CTC_VERSION in ["7.0", "8.0", "9.0"]:
        if EnableVerificationTestConfig:
            args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "RA":
            if clip.file_class in ["A1", "E", "G1"]:
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.file_class in ["A2", "B1"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "AS":
            # use the same configuration as RA
            if (clip.width == 3840 and clip.height == 2160) or (
                clip.width == 2560 and clip.height == 1440
            ):
                # 4 column tiles should be used
                args += " --tile-rows=0 --tile-columns=2 --threads=4 --row-mt=0 "
            elif clip.width == 1920 and clip.height == 1080:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif test_cfg == "LD":
            if clip.file_class in ["A2", "B1"]:
                # 8 tiles should be used
                args += " --tile-rows=1 --tile-columns=2 --threads=8 --row-mt=0 "
            elif clip.file_class in ["A3"]:
                # 2 column tiles should be used
                args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
            else:
                # 1 tile should be used
                args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "
        elif (test_cfg in ["AI", "STILL"]) and (
            clip.width >= 3840 and clip.height >= 2160
        ):
            # 2 tiles should be used
            args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    elif clip.width >= 3840 and clip.height >= 2160:
        args += " --tile-rows=0 --tile-columns=1 --threads=2 --row-mt=0 "
    else:
        args += " --tile-rows=0 --tile-columns=0 --threads=1 --row-mt=0 "

    if EnableOpenGOP:
        args += " --enable-fwd-kf=1 "
    else:
        args += " --enable-fwd-kf=0 "

    if EnableTemporalFilter:
        args += " --enable-keyframe-filtering=1 "
    else:
        args += " --enable-keyframe-filtering=0 "

    if test_cfg == "AI" or test_cfg == "STILL":
        args += " --kf-min-dist=0 --kf-max-dist=0 "
    elif test_cfg == "RA" or test_cfg == "AS":
        args += (
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d"
            " --gf-max-pyr-height=%d --kf-min-dist=%d --kf-max-dist=%d"
            " --lag-in-frames=%d --auto-alt-ref=1 "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
                GOP_SIZE,
                GOP_SIZE,
                SUB_GOP_SIZE + 3,
            )
        )
    elif test_cfg == "LD":
        args += (
            " --kf-min-dist=9999 --kf-max-dist=9999 --lag-in-frames=0"
            " --min-gf-interval=%d --max-gf-interval=%d --gf-min-pyr-height=%d "
            " --gf-max-pyr-height=%d "
            % (
                SUB_GOP_SIZE,
                SUB_GOP_SIZE,
                math.log2(SUB_GOP_SIZE),
                math.log2(SUB_GOP_SIZE),
            )
        )
    else:
        print("Unsupported Test Configuration %s" % test_cfg)

    if clip.file_class == "G1" or clip.file_class == "G2":
        args += (
            "--color-primaries=bt2020 --transfer-characteristics=smpte2084 "
            "--matrix-coefficients=bt2020ncl --chroma-sample-position=colocated "
        )

    args += " -o %s %s" % (outfile, clip.file_path)
    cmd = AV1ENC + args + "> %s 2>&1" % enc_log
    if EnableTimingInfo:
        if Platform == "Windows":
            cmd = "ptime " + cmd + " >%s" % enc_perf
        elif Platform == "Darwin":
            cmd = "gtime --verbose --output=%s " % enc_perf + cmd
        else:
            if UsePerfUtil:
                cmd = "3>%s perf stat --log-fd 3 " % enc_perf + cmd
            else:
                cmd = "/usr/bin/time --verbose --output=%s " % enc_perf + cmd
    ExecuteCmd(cmd, LogCmdOnly)


def EncodeWithSVT_AV1(
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    # encode with SVT-AV1 with the best possible quality without constraint
    args = (
        " --preset %s --lp 2 -n %d  -q %d -w %d -h %d  --fps-num %d --fps-denom %d --input-depth %d "
        % (
            str(preset),
            framenum,
            QP,
            clip.width,
            clip.height,
            clip.fps_num,
            clip.fps_denom,
            clip.bit_depth,
        )
    )

    # For 4K clip, encode with 2 tile columns using two threads.
    # --tile-columns value is in log2.
    if EnableVerificationTestConfig:
        args += " "
    elif clip.width >= 3840 and clip.height >= 2160:
        args += " --tile-columns 1 "
    else:
        args += " "

    if test_cfg == "AI" or test_cfg == "STILL":
        args += " --keyint 255 "
    elif test_cfg == "RA" or test_cfg == "AS":
        args += " --keyint %d " % (GOP_SIZE)
    elif test_cfg == "LD":
        args += " --keyint -1 --pred-struct 1 "
    else:
        print("Unsupported Test Configuration %s" % test_cfg)

    if clip.file_class == "G1" or clip.file_class == "G2":
        args += "--enable-hdr 1 "

    args += "-i %s -b %s" % (clip.file_path, outfile)
    cmd = SVTAV1 + args + "> %s 2>&1" % enc_log
    if EnableTimingInfo:
        if Platform == "Windows":
            cmd = "ptime " + cmd + " >%s" % enc_perf
        elif Platform == "Darwin":
            cmd = "gtime --verbose --output=%s " % enc_perf + cmd
        else:
            if UsePerfUtil:
                cmd = "3>%s perf stat --log-fd 3 " % enc_perf + cmd
            else:
                cmd = "/usr/bin/time --verbose --output=%s " % enc_perf + cmd
    ExecuteCmd(cmd, LogCmdOnly)


def EncodeWithHM_HEVC(
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    input_yuv_file = GetShortContentName(outfile, False) + ".yuv"
    bs_path = os.path.dirname(outfile)
    input_yuv_file = os.path.join(bs_path, input_yuv_file)
    ConvertY4MToYUV(clip, input_yuv_file, LogCmdOnly)

    args = (
        " -c %s -i %s -b %s --SourceWidth=%d --SourceHeight=%d --InputBitDepth=%d --InternalBitDepth=%d "
        " --InputChromaFormat=420 --FrameRate=%d --GOPSize=%d --FramesToBeEncoded=%d --QP=%d "
        % (
            HEVCCfgFile,
            input_yuv_file,
            outfile,
            clip.width,
            clip.height,
            clip.bit_depth,
            clip.bit_depth,
            clip.fps,
            SUB_GOP_SIZE,
            framenum,
            QP,
        )
    )

    args += " --ConformanceWindowMode=1 "  # needed to support non multiple of 8 resolutions.

    # enable open Gop
    if EnableOpenGOP:
        args += " --DecodingRefreshType=1 "
    else:
        args += " --DecodingRefreshType=2 "

    if EnableTemporalFilter:
        args += " --TemporalFilter=1 "
    else:
        args += " --TemporalFilter=0 "

    if test_cfg == "AI" or test_cfg == "STILL":
        args += " --IntraPeriod=1 "
    elif test_cfg == "RA" or test_cfg == "AS":
        args += " --IntraPeriod=%d " % GOP_SIZE
    elif test_cfg == "LD":
        args += " --IntraPeriod=-1 "
    else:
        print("Unsupported Test Configuration %s" % test_cfg)

    cmd = HMENC + args + "> %s 2>&1" % enc_log
    if EnableTimingInfo:
        if Platform == "Windows":
            cmd = "ptime " + cmd + " >%s" % enc_perf
        elif Platform == "Darwin":
            cmd = "gtime --verbose --output=%s " % enc_perf + cmd
        else:
            if UsePerfUtil:
                cmd = "3>%s perf stat --log-fd 3 " % enc_perf + cmd
            else:
                cmd = "/usr/bin/time --verbose --output=%s " % enc_perf + cmd
    ExecuteCmd(cmd, LogCmdOnly)

    DeleteFile(input_yuv_file, LogCmdOnly)


def VideoEncode(
    EncodeMethod,
    CodecName,
    clip,
    test_cfg,
    QP,
    framenum,
    outfile,
    preset,
    enc_perf,
    enc_log,
    start_frame=0,
    LogCmdOnly=False,
):
    Utils.CmdLogger.write("::Encode\n")
    if CodecName == "av2":
        if EncodeMethod == "aom":
            EncodeWithAOM_AV2(
                clip,
                test_cfg,
                QP,
                framenum,
                outfile,
                preset,
                enc_perf,
                enc_log,
                start_frame,
                LogCmdOnly,
            )
    elif CodecName == "av1":
        if EncodeMethod == "aom":
            EncodeWithAOM_AV1_Constrained(
                clip,
                test_cfg,
                QP,
                framenum,
                outfile,
                preset,
                enc_perf,
                enc_log,
                start_frame,
                LogCmdOnly,
            )
        elif EncodeMethod == "svt":
            EncodeWithSVT_AV1(
                clip,
                test_cfg,
                QP,
                framenum,
                outfile,
                preset,
                enc_perf,
                enc_log,
                start_frame,
                LogCmdOnly,
            )
        else:
            raise ValueError("invalid parameter for encode.")
    elif CodecName == "hevc":
        if EncodeMethod == "hm":
            EncodeWithHM_HEVC(
                clip,
                test_cfg,
                QP,
                framenum,
                outfile,
                preset,
                enc_perf,
                enc_log,
                start_frame,
                LogCmdOnly,
            )
        else:
            raise ValueError("invalid parameter for encode.")
    else:
        raise ValueError("invalid parameter for encode.")
