add support for AV2 CTC test for RA and LD configuration

1. use a class to define the clip information
2. add support for using HDRConvert to do downscaling and upscaling.
3. use vmaf_rc to calculate all quality metrics
4. add support for regular AV2 CTC test for RA and LD
5. add all AV2 CTC test clip names and y4m files

Change-Id: If0d25fa16ca06dccfb532e7a60c94d50c132d189
diff --git a/tools/convexhull_framework/README.TXT b/tools/convexhull_framework/README.TXT
index 22e1314..599dd1d 100644
--- a/tools/convexhull_framework/README.TXT
+++ b/tools/convexhull_framework/README.TXT
@@ -10,13 +10,11 @@
 
 Prerequisites
 1. Inside ./bin folder, make sure you have executables for all external tools,
-   such as vmafossexec, ffmpeg, HDRMetrics, encoder, decoder, etc. You can get
-   the pre-build binary or source code for the following tools that are needed:
-   ffmpeg: https://ffmpeg.org/download.html
+   such as vmaf_rc, HDRConvert, encoder, decoder, etc. You can get the pre-build
+   binaries or source code for the following tools that are needed:
    vmaf tools: https://github.com/Netflix/vmaf
-   HDRMetrics: https://gitlab.com/standards/HDRTools
+   HDRConvert: https://gitlab.com/standards/HDRTools
    libaom AV1 encoder/decoder: https://aomedia.googlesource.com/aom/
-   SVT HEVC encoder: https://github.com/OpenVisualCloud/SVT-HEVC
    SVT AV1 encoder: https://github.com/OpenVisualCloud/SVT-AV1
    Please always follow the manual of these tools to update the command
    parameters used in the script
@@ -25,9 +23,10 @@
    (vmaf_v0.6.1.pkl.model, vmaf_v0.6.1.pkl) are used and stored under the ./bin
    folder.
 
-3. HDRMetrics requires a template config file (HDRMetricsYUV420_template.cfg).
-   It is also located under the ./bin folder. Test framework will generate
-   individual config file based on this template.
+3. HDRConvert is used to perform downscale and upscale operation. It requires
+   a template config file (HDRConvScalerY4MFile.cfg). It is also located under
+   the ./bin folder. Test framework will generate individual config file based
+   on this template.
 
 4. VBA macro binary file (vbaProject-AV2.bin) for bdrate calculation is also
    stored under ./bin folder, which is needed for BDRATE calculation inside the
@@ -36,45 +35,42 @@
 5. Test framework is implemented in python 3. It requires few extra python
    packages, such as xlrd, xlsxwriter, argparse, etc.
 
-Things you need to update inside ./src/Config.py to configure the test.
-1. Update the Clips table following the existing example to provide the list of
-   video sequences that you want to test. The first value is the yuv file name
-   without .yuv suffix. All test video sequences should be in the folder
-   specified by ContentPath. Test framework will extract the string before the
-   first "_" as the short name to identify the test sequence and use that to
-   generate file name of other intermediate files, so please make sure the short
-   name of the test sequence is unique. Currently only 8 bit yuv sequences in
-   yuv420p format are supported.
+Things you need to update to configure the test.
+1. Update the test clips table in AV2CTCVideo.py file following the existing
+   example to provide the list and classes of video sequences that you want to
+   test. Currently, only .y4m files are supported. Test script will parse the
+   y4m file header to get the basic information of the video clip, such as
+   resolution, frame rate, bit depth and color format, etc.
 
-2. Update FrameNum to specify number of frames you want to process.
+2. Update the test configuration list in ./src/Config.py to specify the test
+   configurations. Currently, supported test configurations include Random Access
+   (RA), Low Delay (LD) and Adaptive Streaming (AS).
 
-3. Update DnScaleRatio list to provide the list of downscaling ratios.
+3. Update FrameNum in ./src/Config.py to specify number of frames you want to
+   process.
 
-4. Update DnScalingAlgos and UpScalingAlgos list to specify downscaling/upscaling
-   filter types that you want to test. The name of the filter types is that
-   supported by ffmpeg. The size of these two lists must be the same. Filter
-   types for downscaling and upscaling can be different.
+4. Update DnScaleRatio list in ./src/Config.py to provide the list of downscaling
+   ratios used for adaptive streaming (convex hull) test configuration.
 
-5. Update QPs list to specify the list of QPs you want to test for different
-   codecs. QPs must be in the valid QP range for different coding standards.
-   For example, [0, 51] for hevc and [0, 63] for AV1
+5. Update DnScalingAlgos and UpScalingAlgos list in ./src/Config.py to specify
+   downscaling/upscaling filter types that you want to test. The name of the
+   filter types is that supported by HDRConvert. The size of these two lists
+   must be the same. Filter types for downscaling and upscaling can be different.
+   Right now, only the lanczos filter is supported.
 
-6. Update QualityEvalMethods to specify tools you want to use to calculate
-   quality metrics in the QualityList. Currently, only VMAF and HDRTools are
-   supported. You also need to update QualityList and QualityEvalMethods to
-   specify what quality metrics you want to calculate. Test framework will filter
-   out other metrics and only report the metrics that are specified.
+6. Update QPs list in ./src/Config.py to specify the list of QPs you want to
+   test for different codecs. QPs must be in the valid QP range for different
+   coding standards. For example, [0, 63] for AV1.
 
-7. Update LogCmdOnly flag. When it is set to True, the test framework will only
-   capture all process command sequences into a log file (under ./test/testLogs
-   folder with time stamp) without actually running it on your system. This
-   feature is to support the use case in which actual processing tasks need to
-   distributed on to a server cluster and executed in parallel.
+7. Update QualityList in ./src/Config.py to specify quality metrics you want to
+   calculate. Currently, only VMAF_RC is supported for quality metrics calculation.
+   It supports PSNR (for Y, U, V planes), SSIM, MS-SSIM and VMAF for Y plane only.
 
 8. Update SMOKE_TEST flag, It is used for sanity check purpose, in which only
    few frames are processed to reduce the test time.
 
-Command lines for running the tests:
+Sample command lines for running the adaptive streaming (convex hull) test:
+"python ConvexHullTest.py [options]"
 below is the full command line options in help message:
   -h, --help                   show this help message and exit
   -f, --function               function to run: clean, scaling, sumscaling,
@@ -84,12 +80,20 @@
   -s, --SaveMemory             [0|1] save memory mode will delete most files in
                                intermediate steps and keeps only necessary ones
                                for RD calculation. It is false by default
+  -CmdOnly, --LogCmdOnly       LogCmdOnly mode will only capture the command
+                               sequences in the command log file instead of
+                               actually run it. It is false by default
   -l, --LoggingLevel           logging level: 0:No Logging, 1: Critical, 2: Error,
                                3:Warning, 4: Info, 5: Debug
-  -c, --CodecName              CodecName: av1 or hevc
-  -m, --EncodeMethod           EncodeMethod: ffmpeg, aom, svt
-  -p, --EncodePreset           EncodePreset: medium, slow, fast, etc for ffmpeg,
-                               0,1,2... for aom and svt
+  -c, --CodecName              CodecName: av1
+  -m, --EncodeMethod           EncodeMethod: aom, svt
+  -p, --EncodePreset           EncodePreset: 0,1,2... for aom and svt
+
+when LogCmdOnly is set to True, the test framework will only capture all process
+command sequences into a log file (under ./test/testLogs folder with time stamp)
+without actually running it on your system. This feature is to support the use
+case in which actual processing tasks need to distributed on to a server cluster
+and executed in parallel.
 
 Sample command for typical operations:
 1.  python ConvexHullTest.py -f clean
@@ -97,14 +101,13 @@
 
 2.  python ConvexHullTest.py -f scaling
     This command will run the standalone downscaling and upscaling tests.
-    Downscaled YUV files are stored under
-    ./test/downscaledYUVs folder. Upscaled YUV files are stored under
-    ./test/upscaledYUVs folder. Quality metrics log files are stored under
-    ./test/qualityLogs folder. Other processing logs and command logs are stored
-    under ./test/testLogs folder. In case HDRMetric is used, individual config
-    files are generated and stored under ./test/configFiles folder. All
-    intermediate file names indicate the input, output resolution and filter types
-    that are used.
+    Downscaled YUV files are stored under ./test/downscaledYUVs folder.
+    Upscaled YUV files are stored under ./test/upscaledYUVs folder.
+    Quality metrics log files are stored under ./test/qualityLogs folder.
+    Other processing logs and command logs are stored under ./test/testLogs folder.
+    For using HDRConvert, individual config files are generated and stored under
+    ./test/configFiles folder. All intermediate file names indicate the input,
+    output resolution and filter types that are used.
 
 3.  python ConvexHullTest.py -f sumscaling
     This command will summarize the quality metrics for the scaling test into
@@ -112,18 +115,17 @@
     each individual test sequence and also excel file that summarizes quality
     result for all test sequences based on classes.
 
-4.  python ConvexHullTest.py -f encode -c hevc -m ffmpeg -p medium
+4.  python ConvexHullTest.py -f encode -c av1 -m aom -p 1
     This command will run the encoding test. It actually contains downscale
-    (optional), encode, decode, upscale, quality metrics steps. Different
-    encoding method and codec name can be specified. For a particular encoder,
-    encoding preset should also be specified. Downscale step will be skipped if
-    downscaled yuv files already generated in ./test/downscaledYUVs. Encoded
+    (optional), encode, decode, upscale, quality metrics steps. Right now, only
+    av1 encoding/decoding with libaom is supported. Downscale step will be skipped
+    if downscaled yuv files already generated in ./test/downscaledYUVs. Encoded
     bitstreams are stored under ./test/bitstreams folder. Decoded YUV files are
     stored under ./test/decodedYUVs folder. Decoded and then upscaled YUV files
     are stored under ./test/decUpscaledYUVs folder. Quality logs are stored under
     ./test/qualityLogs folder.
 
-5.  python ConvexHullTest.py -f convexhull -c hevc -m ffmpeg -p medium
+5.  python ConvexHullTest.py -f convexhull -c av1 -m aom -p 1
     This command will summarize the per sequence quality result based on
     different scaling ratios and scaling filter types.
     Please make sure the same encoding method/codec name/preset are used as the
@@ -139,7 +141,7 @@
     calculate bdrate between two encoding runs to evaluate the quality impact on
     overall convex hull from a coding tool.
 
-6.  python ConvexHullTest.py -f summary -c hevc -m ffmpeg -p medium
+6.  python ConvexHullTest.py -f summary -c av1 -m aom -p 1
     This command will summarize the quality metrics across all test sequences
     into an excel file stored under the ./analysis/summary folder. BDRATE between
     different resolutions will be calculated. Average result based on content
@@ -165,3 +167,35 @@
 a sample command is:
 python ConvexHullBDRate.py -i1 ConvexHullRD_ffmpeg_hevc_medium.xlsx
 -i2 ConvexHullRD_ffmpeg_hevc_veryslow.xlsx -o ConvexHullBDRate.xlsm
+
+Sample command lines for running the regular AV2 CTC test:
+"python AV2CTCTest.py [options]"
+
+below is the full command line options in help message:
+
+  -h, --help                   show this help message and exit
+  -f, --function               function to run: clean, encode, summary
+  -s, --SaveMemory             [0|1] save memory mode will delete most files in
+                               intermediate steps and keeps only necessary ones
+                               for RD calculation. It is false by default
+  -CmdOnly, --LogCmdOnly       LogCmdOnly mode will only capture the command
+                               sequences in the command log file instead of
+                               actually run it. It is false by default
+  -l, --LoggingLevel           logging level: 0:No Logging, 1: Critical, 2: Error,
+                               3:Warning, 4: Info, 5: Debug
+  -p, --EncodePreset           EncodePreset: 0,1,2... for aom
+
+Sample command for typical operations:
+1.  python AV2CTCTest.py -f clean
+    This command will clean up all intermediate files under ./test folder
+
+2.  python AV2CTCTest.py -f encode -p 1
+    This command will run the encoding test. It actually contains encode, decode,
+    and quality metrics steps. Right now, only av1 encoding/decoding with libaom
+    is supported. Encoded bitstreams are stored under ./test/bitstreams folder.
+    Decoded YUV files are stored under ./test/decodedYUVs folder. Quality logs
+    are stored under ./test/qualityLogs folder.
+
+3.  python AV2CTCTest.py -f summary -p 1
+    This command will summarize the quality metrics across all test sequences
+    into an csv file stored under the ./analysis/summary folder.
diff --git a/tools/convexhull_framework/bin/HDRConvScalerY4MFile.cfg b/tools/convexhull_framework/bin/HDRConvScalerY4MFile.cfg
new file mode 100644
index 0000000..466ae7f
--- /dev/null
+++ b/tools/convexhull_framework/bin/HDRConvScalerY4MFile.cfg
@@ -0,0 +1,88 @@
+# HDRConvert default configuration file
+# format: parameter=value or parameter="stringvalue", no SPACES!
+
+###############################################################################
+#
+# Input/output file parameters
+#
+###############################################################################
+SourceFile="D:\YUVs\AV2-CTC\a1_4k\Crosswalk_3840x2160_5994fps_10bit_420.y4m"
+OutputFile="Crosswalk_1920x1080_5994fps_10bit_420.y4m" # Scaled YUV file
+
+SourceWidth=3840         # input frame height
+SourceHeight=2160        # input frame height
+OutputWidth=1920         # input frame height
+OutputHeight=1080        # input frame height
+ScalingMode=3            # Scaling Filter Mode
+LanczosLobes=5
+SourceRate=59.94         # input frame rate
+SourceInterleaved=0      # Interleaved or Planar data
+SourceChromaFormat=1     # Input chroma format
+                         # 0 : 400
+                         # 1 : 420
+                         # 2 : 422
+                         # 3 : 444
+SourceBitDepthCmp0=10    # Input bit depth of luma component
+SourceBitDepthCmp1=10    # Input bit depth of u/cb component
+SourceBitDepthCmp2=10    # Input bit depth of v/cr component
+SourceFourCCCode=6       # FourCC code for input source
+                         # 0: UYVY
+                         # 1: YUY2
+                         # 2: YUYV
+                         # 3: YVYU
+                         # 4: BGR
+                         # 5: V210
+                         # 6: Y444I
+SourceColorSpace=0       # 0: CM_YUV
+                         # 1: CM_RGB
+                         # 2: CM_XYZ
+
+SourceColorPrimaries=0   # 0: BT.709
+                         # 1: BT.2020
+                         # 2: P3D60
+                         # 3: P3D65
+                         # 4: None
+SourceTransferFunction=0
+OutputRate=59.94         # output frame rate
+OutputChromaFormat=1     # Output Chroma format
+                         # 0 : 400
+                         # 1 : 420
+                         # 2 : 422
+                         # 3 : 444
+
+OutputBitDepthCmp0=10     # bit depth of luma component for distortion computation
+OutputBitDepthCmp1=10     # bit depth of u/cb component for distortion computation
+OutputBitDepthCmp2=10    # bit depth of v/cr component for distortion computation
+OutputColorSpace=0       # 0: CM_YUV
+                         # 1: CM_RGB
+                         # 2: CM_XYZ
+
+OutputColorPrimaries=0   # 0: BT.709
+                         # 1: BT.2020
+                         # 2: P3D60
+                         # 3: P3D65
+                         # 4: None
+
+NumberOfFrames=1        # number of frames to process
+InputFileHeader=0        # Input File header to be skipped (in bytes).
+StartFrame=0             # Number of frames to skip before start
+
+SilentMode=1             # Enable Silent mode
+
+OutputTransferFunction=0       # Transfer Function
+                         # 0: NULL (no new TF applied)
+                         # 1: PQ
+                         # 2: PH
+
+USeSingleTransferStep=1  # Use a single step or two step process for the application of the PQ TF
+TransformPrecision=0     # Use fixed (0) or high precision for color transform
+ChromaDownsampleFilter=6 # 444 to 420 conversion filters
+                         # 0: Nearest Neighbor
+                         # 1: Bilinear
+                         # 2: H={1, 6, 1} and V={1, 1}
+                         # 3: H={1, 2, 1} and V={1, 1}
+ChromaUpsampleFilter=0   # 420 to 444 conversion filters
+                         # 0 : Nearest Neighbor
+                         # 1 : 4-tap in w14548
+SetOutputSinglePrec=0    # Set OpenEXR output file precision
+                         # 0: HALF, 1: SINGLE
\ No newline at end of file
diff --git a/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg b/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg
deleted file mode 100644
index 3a21c63..0000000
--- a/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg
+++ /dev/null
@@ -1,99 +0,0 @@
-# HDRMetrics default configuration file
-# format: parameter=value or parameter="stringvalue", no SPACES!
-
-###############################################################################
-#
-# Input file information
-#
-###############################################################################
-Input0File=""    # 1st Input file name
-Input1File=""   # 2nd Input file name
-LogFile=""                                                     # Output Log file name
-NumberOfFrames=10                                              # Number of frames to process
-SilentMode=0                                                   # Enable Silent mode
-MaxSampleValue=10000.0                                         # Maximum sample value for floating point (openEXR) data files
-###############################################################################
-#
-# Metrics
-#
-###############################################################################
-EnableMSSSIM=1                                                 # Enable MSSSIM computation
-EnableSSIM=1 	                                               # Enable SSIM computation
-
-###############################################################################
-#
-# First input parameters
-#
-###############################################################################
-Input0Width=1280                                               # 1st Input source width
-Input0Height=720                                               # 1st Input source height
-Input0ChromaFormat=1                                           # 1st Input Chroma Format
-                                                               # 0 : 400
-                                                               # 1 : 420
-                                                               # 2 : 422
-                                                               # 3 : 444
-Input0Interleaved=0                                            # 1st Input Interleaved Source
-Input0FourCCCode=0                                             # 1st Input Pixel Format
-                                                               # 0: UYVY
-                                                               # 1: YUY2
-                                                               # 2: YUYV
-                                                               # 3: YVYU
-                                                               # 4: BGR
-                                                               # 5: V210
-                                                               # 6: Y444I
-Input0BitDepthCmp0=8                                           # 1st Input Bitdepth Cmp0
-Input0BitDepthCmp1=8                                           # 1st Input Bitdepth Cmp1
-Input0BitDepthCmp2=8                                           # 1st Input Bitdepth Cmp2
-Input0ColorSpace=0                                             # 1st Input Color Space
-                                                               # 0: YUV
-                                                               # 1: RGB
-                                                               # 2: XYZ
-Input0ColorPrimaries=0                                         # 1st Input Color Primaries
-                                                               # 0: BT.709
-                                                               # 1: BT.2020
-                                                               # 2: P3D60
-                                                               # 3: P3D65
-                                                               # 4: None
-Input0FileHeader=0                                             # 1st Input Header (bytes)
-Input0StartFrame=0                                             # 1st Input Start Frame
-Input0FrameSkip=0                                              # 1st Input Frame Skipping
-Input0Rate=24.0                                                # 1st Input Frame Rate
-
-###############################################################################
-#
-# Second input source parameters
-#
-###############################################################################
-Input1Width=1280                                               # 2nd Input source width
-Input1Height=720                                               # 2nd Input source height
-Input1ChromaFormat=1                                           # 2nd Input Chroma Format
-                                                               # 0 : 400
-                                                               # 1 : 420
-                                                               # 2 : 422
-                                                               # 3 : 444
-Input1Interleaved=0                                            # 2nd Input Interleaved Source
-Input1FourCCCode=0                                             # 2nd Input Pixel Format
-                                                               # 0: UYVY
-                                                               # 1: YUY2
-                                                               # 2: YUYV
-                                                               # 3: YVYU
-                                                               # 4: BGR
-                                                               # 5: V210
-                                                               # 6: Y444I
-Input1BitDepthCmp0=8                                           # 2nd Input Bitdepth Cmp0
-Input1BitDepthCmp1=8                                           # 2nd Input Bitdepth Cmp1
-Input1BitDepthCmp2=8                                           # 2nd Input Bitdepth Cmp2
-Input1ColorSpace=0                                             # 2nd Input Color Space
-                                                               # 0: YUV
-                                                               # 1: RGB
-                                                               # 2: XYZ
-Input1ColorPrimaries=0                                         # 2nd Input Color Primaries
-                                                               # 0: BT.709
-                                                               # 1: BT.2020
-                                                               # 2: P3D60
-                                                               # 3: P3D65
-                                                               # 4: None
-Input1FileHeader=0                                             # 2nd Input Header (bytes)
-Input1StartFrame=0                                             # 2nd Input Start Frame
-Input1FrameSkip=0                                              # 2nd Input Frame Skipping
-Input0Rate=24.0                                                # 2nd Input Frame Rate
diff --git a/tools/convexhull_framework/src/AV2CTCTest.py b/tools/convexhull_framework/src/AV2CTCTest.py
new file mode 100644
index 0000000..7efc8c3
--- /dev/null
+++ b/tools/convexhull_framework/src/AV2CTCTest.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import sys
+import argparse
+from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
+from Utils import GetShortContentName, CreateNewSubfolder, SetupLogging, \
+     Cleanfolder, CreateClipList
+import Utils
+from Config import LogLevels, FrameNum, TEST_CONFIGURATIONS, QPs, WorkPath, \
+     Path_RDResults, LoggerName,QualityList
+from EncDecUpscale import Encode, Decode
+
+###############################################################################
+##### Helper Functions ########################################################
+def CleanIntermediateFiles():
+    folders = [Path_DecodedYuv, Path_CfgFiles]
+    for folder in folders:
+        Cleanfolder(folder)
+
+def GetRDResultCsvFile(EncodeMethod, CodecName, EncodePreset, test_cfg):
+    filename = "RDResults_%s_%s_%s_Preset_%s.csv" % \
+               (EncodeMethod, CodecName, test_cfg, EncodePreset)
+    file = os.path.join(Path_RDResults, filename)
+    return file
+
+def GetBsReconFileName(EncodeMethod, CodecName, EncodePreset, test_cfg, clip, QP):
+    basename = GetShortContentName(clip.file_name, False)
+    filename = "%s_%s_%s_%s_Preset_%s_QP_%d.ivf" % \
+               (basename, EncodeMethod, CodecName, test_cfg, EncodePreset, QP)
+    bs = os.path.join(Path_Bitstreams, filename)
+    filename = "%s_%s_%s_%s_Preset_%s_QP_%d_Decoded.y4m" % \
+               (basename, EncodeMethod, CodecName, test_cfg, EncodePreset, QP)
+    dec = os.path.join(Path_DecodedYuv, filename)
+    return bs, dec
+
+
+def setupWorkFolderStructure():
+    global Path_Bitstreams, Path_DecodedYuv, Path_QualityLog, Path_TestLog,\
+           Path_CfgFiles
+    Path_Bitstreams = CreateNewSubfolder(WorkPath, "bistreams")
+    Path_DecodedYuv = CreateNewSubfolder(WorkPath, "decodedYUVs")
+    Path_QualityLog = CreateNewSubfolder(WorkPath, "qualityLogs")
+    Path_TestLog = CreateNewSubfolder(WorkPath, 'testLogs')
+    Path_CfgFiles = CreateNewSubfolder(WorkPath, "configFiles")
+
+###############################################################################
+######### Major Functions #####################################################
+def CleanUp_workfolders():
+    folders = [Path_Bitstreams, Path_DecodedYuv, Path_QualityLog,
+               Path_TestLog, Path_CfgFiles]
+    for folder in folders:
+        Cleanfolder(folder)
+
+def Run_Encode_Test(test_cfg, clip, preset, LogCmdOnly = False):
+    Utils.Logger.info("start running %s encode tests with %s"
+                      % (test_cfg, clip.file_name))
+
+    for QP in QPs:
+        Utils.Logger.info("start encode with QP %d" % (QP))
+        #encode
+        if LogCmdOnly:
+            Utils.CmdLogger.write("============== Job Start =================n")
+        bsFile = Encode('aom', 'av1', preset, clip, test_cfg, QP, FrameNum,
+                        Path_Bitstreams, LogCmdOnly)
+        Utils.Logger.info("start decode file %s" % os.path.basename(bsFile))
+        #decode
+        decodedYUV = Decode('av1', bsFile, Path_DecodedYuv, LogCmdOnly)
+        #calcualte quality distortion
+        Utils.Logger.info("start quality metric calculation")
+        CalculateQualityMetric(clip.file_path, FrameNum, decodedYUV, clip.fmt,
+                               clip.width, clip.height, clip.bit_depth,
+                               Path_QualityLog, LogCmdOnly)
+        if SaveMemory:
+            Cleanfolder(Path_DecodedYuv)
+        Utils.Logger.info("finish running encode with QP %d" % (QP))
+        if LogCmdOnly:
+            Utils.CmdLogger.write("============== Job End ===================\n\n")
+
+def GenerateSummaryRDDataFile(EncodeMethod, CodecName, EncodePreset,
+                              test_cfg, clip_list):
+    Utils.Logger.info("start saving RD results to excel file.......")
+    if not os.path.exists(Path_RDResults):
+        os.makedirs(Path_RDResults)
+
+    csv_file = GetRDResultCsvFile(EncodeMethod, CodecName, EncodePreset, test_cfg)
+    csv = open(csv_file, 'wt')
+    csv.write("File,Class,Width,Height,TestCfg,EncodeMethod,CodecName,"\
+              "EncodePreset,QP,Bitrate(kbps)")
+    for qty in QualityList:
+        csv.write(',' + qty)
+    csv.write('\n')
+
+    for clip in clip_list:
+        for qp in QPs:
+            bs, dec = GetBsReconFileName(EncodeMethod, CodecName, EncodePreset,
+                                         test_cfg, clip, qp)
+            bitrate = (os.path.getsize(bs) * 8 * (clip.fps_num / clip.fps_denom)
+                       / FrameNum) / 1000.0
+            quality = GatherQualityMetrics(dec, Path_QualityLog)
+            csv.write("%s,%s,%d,%d,%s,%s,%s,%s,%d,%.4f"
+                      %(clip.file_name, clip.file_class, clip.width, clip.height,
+                      test_cfg, EncodeMethod, CodecName,EncodePreset,qp,bitrate))
+            for qty in quality:
+                csv.write(",%.4f"%qty)
+            csv.write("\n")
+
+    Utils.Logger.info("finish export RD results to file.")
+    return
+
+def ParseArguments(raw_args):
+    parser = argparse.ArgumentParser(prog='AV2CTCTestTest.py',
+                                     usage='%(prog)s [options]',
+                                     description='')
+    parser.add_argument('-f', '--function', dest='Function', type=str,
+                        required=True, metavar='',
+                        choices=["clean", "encode", "summary"],
+                        help="function to run: clean, encode, summary")
+    parser.add_argument('-s', "--SaveMemory", dest='SaveMemory', type=bool,
+                        default=False, metavar='',
+                        help="save memory mode will delete most files in"
+                             " intermediate steps and keeps only necessary "
+                             "ones for RD calculation. It is false by default")
+    parser.add_argument('-CmdOnly', "--LogCmdOnly", dest='LogCmdOnly', type=bool,
+                        default=False, metavar='',
+                        help="LogCmdOnly mode will only capture the command sequences"
+                             "It is false by default")
+    parser.add_argument('-l', "--LoggingLevel", dest='LogLevel', type=int,
+                        default=3, choices=range(len(LogLevels)), metavar='',
+                        help="logging level: 0:No Logging, 1: Critical, 2: Error,"
+                             " 3: Warning, 4: Info, 5: Debug")
+    parser.add_argument('-p', "--EncodePreset", dest='EncodePreset', type=str,
+                        metavar='', help="EncodePreset: 0,1,2... for aom")
+    if len(raw_args) == 1:
+        parser.print_help()
+        sys.exit(1)
+    args = parser.parse_args(raw_args[1:])
+
+    global Function, SaveMemory, LogLevel, EncodePreset, LogCmdOnly
+    Function = args.Function
+    SaveMemory = args.SaveMemory
+    LogLevel = args.LogLevel
+    EncodePreset = args.EncodePreset
+    LogCmdOnly = args.LogCmdOnly
+
+######################################
+# main
+######################################
+if __name__ == "__main__":
+    #sys.argv = ["", "-f", "encode", "-p","6"]
+    #sys.argv = ["", "-f", "summary", "-p", "6"]
+    ParseArguments(sys.argv)
+
+    # preparation for executing functions
+    setupWorkFolderStructure()
+    if Function != 'clean':
+        SetupLogging(LogLevel, LogCmdOnly, LoggerName, Path_TestLog)
+        clip_list = CreateClipList('RA')
+
+    # execute functions
+    if Function == 'clean':
+        CleanUp_workfolders()
+    elif Function == 'encode':
+        for test_cfg in TEST_CONFIGURATIONS:
+            for clip in clip_list:
+                Run_Encode_Test(test_cfg, clip, EncodePreset, LogCmdOnly)
+    elif Function == 'summary':
+        for test_cfg in TEST_CONFIGURATIONS:
+            GenerateSummaryRDDataFile('aom', 'av1', EncodePreset,
+                                      test_cfg, clip_list)
+        Utils.Logger.info("RD data summary file generated")
+    else:
+        Utils.Logger.error("invalid parameter value of Function")
diff --git a/tools/convexhull_framework/src/AV2CTCVideo.py b/tools/convexhull_framework/src/AV2CTCVideo.py
new file mode 100644
index 0000000..9d6b4b7
--- /dev/null
+++ b/tools/convexhull_framework/src/AV2CTCVideo.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+"""
+Python file for definition of AV2 CTC testing clips/sets
+"""
+
+#TEST_SET = ["a1_4k", "a2_2k", "a3_720p", "a4_360p", "a5_270p", "b1_syn", "hdr"]
+CTC_TEST_SET = ["a5_270p"]
+AS_TEST_SET = ["a1_4k"]
+Y4M_CLIPs = {
+"a1_4k"         : ["Crosswalk_3840x2160_5994fps_10bit_420.y4m"],
+'''
+"a1_4k"         : ["BoxingPractice_3840x2160_5994fps_10bit_420.y4m", "Crosswalk_3840x2160_5994fps_10bit_420.y4m",
+                   "FoodMarket2_3840x2160_5994fps_10bit_420.y4m", "Neon1224_3840x2160_2997fps.y4m",
+                   "NocturneDance_3840x2160p_10bit_60fps.y4m", "PierSeaSide_3840x2160_2997fps_10bit_420.y4m",
+                   "Tango_3840x2160_5994fps_10bit_420.y4m", "TimeLapse_3840x2160_5994fps_10bit_420.y4m"],
+'''
+"a2_2k"         : ["Aerial3200_1920x1080_5994_10bit_420.y4m", "Boat_1920x1080_60fps_10bit_420.y4m",
+                   "crowd_run_1080p50.y4m", "FoodMarket_1920x1080_60fps_10bit_420.y4m",
+                   "Meridian_talk_sdr_1080p_10bit.y4m", "old_town_cross_1080p50.y4m",
+                   "pedestrian_area_1080p25.y4m", "raw_1080x1920@30.walking.in.street.y4m",
+                   "RitualDance_1920x1080_60fps_10bit_420.y4m", "riverbed_1080p25.y4m",
+                   "rush_field_cuts_1080p.y4m", "Skater227_1920x1080_30fps.y4m",
+                   "TunnelFlag_1920x1080_60fps_10bit_420.y4m", "Vertical_bees_2997_1080x1920.y4m",
+                   "Vertical_Carnaby_5994_1080x1920.y4m", "WorldCup_1920x1080_30p.y4m",
+                   "WorldCup_far_1920x1080_30p.y4m"],
+"a3_720p"       : ["controlled_burn_720p_420.y4m", "Johnny_1280x720_60.y4m",
+                   "KristenAndSara_1280x720_60.y4m", "Netflix_DrivingPOV_720p_60fps_10bit_420.y4m",
+                   "Netflix_RollerCoaster_720p_60fps_10bit_420.y4m", "vidyo3_720p_60fps.y4m",
+                   "vidyo4_720p_60fps.y4m", "west_wind_easy_720p_420.y4m"],
+"a4_360p"       : ["blue_sky_360p25.y4m", "red_kayak_360p.y4m",
+                   "snow_mnt_640x360p.y4m", "speed_bag_640x360p.y4m",
+                   "stockholm_640x360p60.y4m", "touchdown_pass_640x360p30.y4m"],
+"a5_270p"       : ["FourPeople_480x270_60.y4m", "park_joy_480x270_50.y4m",
+                   "sparks_elevator_480x270p_60.y4m", "Vertical_Bayshore_2997_270x480.y4m"],
+"b1_syn"        : ["AOV5_1920x1080_60_8bit_420.y4m", "baolei_2048x1080_60fps.y4m",
+                   "cosmos_log_2048x858_25_8bit_sdr.y4m", "EuroTruckSimulator2.y4m",
+                   "GlassHalf_1920x1080p_24p_8b_420.y4m", "life_1080p30.y4m",
+                   "MINECRAFT_1080p_60_8bit.y4m", "MissionControlClip3_1920x1080_60_420.y4m",
+                   "sol_levante_dragons_sdr_1920x1080_24_10bit_full.y4m", "sol_levante_face_sdr_1920x1080_24_10bit.y4m",
+                   "wikipedia_1920x1080p30.y4m"],
+"hdr"           : ["cosmos_caterpillar_2048x858p24_hdr10.y4m", "cosmos_tree_trunk_2048x858p24_hdr10.y4m",
+                   "meridian_shore_1920x1080p60_hdr10.y4m", "meridian_talk_1920x1080p60_hdr10.y4m",
+                   "merid_road_3840x2160p60_hdr10.y4m", "nocturne_dance_3840x2160_hdr10.y4m",
+                   "nocturne_room_3840x2160_hdr10.y4m", "sol_levante_dragons_1920x1080p24_hdr10.y4m",
+                   "sol_levante_face_1920x1080p24_hdr10.y4m", "sparks_truck_2048x1080p60_hdr10.y4m",
+                   "sparks_welding_4096x2160p60_hdr10.y4m"],
+}
diff --git a/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py b/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py
deleted file mode 100644
index 0aa1301..0000000
--- a/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env python
-## Copyright (c) 2019, Alliance for Open Media. All rights reserved
-##
-## This source code is subject to the terms of the BSD 2 Clause License and
-## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
-## was not distributed with this source code in the LICENSE file, you can
-## obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
-##
-__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
-
-import os
-import re
-import logging
-from Config import BinPath, LoggerName, LogCmdOnly, FFMPEG
-from Utils import GetShortContentName, ExecuteCmd
-
-subloggername = "CalcQtyMetrics_FFMPEGTool"
-loggername = LoggerName + '.' + '%s' % subloggername
-logger = logging.getLogger(loggername)
-
-
-FFMPEGMetricsFullList = ['PSNR_Y', 'PSNR_U', 'PSNR_V']
-
-def ParseFfmpegLogFile(psnr_log):
-    floats = len(FFMPEGMetricsFullList) * [0.0]
-    flog = open(psnr_log, 'r')
-    cnt = 0
-    for line in flog:
-        cnt += 1
-        item = re.findall(r"psnr_y:(\d+\.?\d*)", line)
-        floats[0] += 0 if len(item) == 0 else float(item[0])
-        item = re.findall(r"psnr_u:(\d+\.?\d*)", line)
-        floats[1] += 0 if len(item) == 0 else float(item[0])
-        item = re.findall(r"psnr_v:(\d+\.?\d*)", line)
-        floats[2] += 0 if len(item) == 0 else float(item[0])
-
-    floats = [float(i) / cnt for i in floats]
-
-    print_str = "FFMPEG quality metrics: "
-    for metrics, idx in zip(FFMPEGMetricsFullList, range(len(FFMPEGMetricsFullList))):
-        print_str += "%s = %2.5f, " % (metrics, floats[idx])
-    logger.info(print_str)
-
-    return floats[0:len(FFMPEGMetricsFullList)]
-
-
-def GetFfmpegLogFile(recfile, path):
-    filename = GetShortContentName(recfile, False) + '_psnr.log'
-    file = os.path.join(path, filename)
-    return file
-
-################################################################################
-##################### Exposed Functions ########################################
-def FFMPEG_CalQualityMetrics(origfile, recfile, num, w, h, logfilePath):
-    #calculate psnr using ffmpeg filter to get psnr_u and psnr_v
-    #here we have to pass the psnr_log file name to FFMPEG without the target
-    #log path first, then copy the result psnr log to the target log path after
-    #the ffmpeg call. it doesn't work if the full path is directly passed to FFMPEG
-    psnr_log = GetFfmpegLogFile(recfile, BinPath)
-    psnr_log = os.path.basename(psnr_log)
-    args = " -s %dx%d -pix_fmt yuv420p -i %s -s %dx%d -pix_fmt yuv420p -i %s" \
-           " -frames:v %d -lavfi psnr=%s -f null -" % \
-           (w, h, origfile, w, h, recfile, num, psnr_log)
-    cmd = FFMPEG + args
-    ExecuteCmd(cmd, LogCmdOnly)
-    #move the psnr log to the target log path
-    target = os.path.join(logfilePath, psnr_log)
-    cmd = "move %s %s" % (psnr_log, target)
-    ExecuteCmd(cmd, LogCmdOnly)
-
-def FFMPEG_GatherQualityMetrics(recfile, logfilePath):
-    psnr_log = GetFfmpegLogFile(recfile, logfilePath)
-    results = ParseFfmpegLogFile(psnr_log)
-    return results
diff --git a/tools/convexhull_framework/src/CalcQtyWithHdrTools.py b/tools/convexhull_framework/src/CalcQtyWithHdrTools.py
deleted file mode 100644
index 9593c87..0000000
--- a/tools/convexhull_framework/src/CalcQtyWithHdrTools.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env python
-## Copyright (c) 2019, Alliance for Open Media. All rights reserved
-##
-## This source code is subject to the terms of the BSD 2 Clause License and
-## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
-## was not distributed with this source code in the LICENSE file, you can
-## obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent.
-##
-__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
-
-from shutil import copyfile
-import fileinput
-import os
-import re
-from Utils import GetShortContentName, ExecuteCmd
-from Config import BinPath, LogCmdOnly, LoggerName, HDRTool
-import logging
-
-subloggername = "HDRToolsRun"
-loggername = LoggerName + '.' + '%s' % subloggername
-logger = logging.getLogger(loggername)
-
-
-HDRToolsConfigFileTemplate = os.path.join(BinPath, 'HDRMetricsYUV420_template.cfg')
-HDRToolsMetricsFullList = ['PSNR_Y', 'PSNR_U', 'PSNR_V', 'MS-SSIM_Y', 'MS-SSIM_U',
-                           'MS-SSIM_V', 'SSIM_Y', 'SSIM_U', 'SSIM_V']
-
-def GenerateCfgFile(orig_file, rec_file, num, w, h, configpath):
-    filename = GetShortContentName(rec_file, False) + '_hdrtools.cfg'
-    cfgfile = os.path.join(configpath, filename)
-    copyfile(HDRToolsConfigFileTemplate, cfgfile)
-    fp = fileinput.input(cfgfile, inplace=1)
-    for line in fp:
-        if 'Input0File=' in line:
-            line = 'Input0File="%s"\n' % orig_file
-        if 'Input1File=' in line:
-            line = 'Input1File="%s"\n' % rec_file
-        if 'NumberOfFrames=' in line:
-            line = 'NumberOfFrames=%d\n' % num
-        if 'Input0Width=' in line:
-            line = 'Input0Width=%d\n' % w
-        if 'Input1Width=' in line:
-            line = 'Input1Width=%d\n' % w
-        if 'Input0Height=' in line:
-            line = 'Input0Height=%d\n' % h
-        if 'Input1Height=' in line:
-            line = 'Input1Height=%d\n' % h
-        print(line, end='')
-    fp.close()
-    return cfgfile
-
-def ParseHDRToolLogFile(logfile):
-    flog = open(logfile, 'r')
-    pattern = re.compile(r'\d+\.?\d*')
-    floats = len(HDRToolsMetricsFullList) * [0.0]
-    for line in flog:
-        if 'D_Avg' in line:
-            floats = [float(i) for i in pattern.findall(line)]
-            break
-
-    print_str = "HDRTools qty metrics: "
-    for metrics, idx in zip(HDRToolsMetricsFullList,
-                            range(len(HDRToolsMetricsFullList))):
-        print_str += "%s = %2.5f, " % (metrics, floats[idx])
-    logger.info(print_str)
-    return floats[0:len(HDRToolsMetricsFullList)]
-
-def GetHDRLogFile(recfile, path):
-    filename = GetShortContentName(recfile, False) + '_hdrtools.log'
-    file = os.path.join(path, filename)
-    return file
-
-################################################################################
-##################### Exposed Functions ########################################
-def HDRTool_CalQualityMetrics(origfile, recfile, num, w, h, logfilepath,
-                              cfgfilepath):
-    cfgFile = GenerateCfgFile(origfile, recfile, num, w, h, cfgfilepath)
-    logfile = GetHDRLogFile(recfile, logfilepath)
-    args = " -f %s > %s" % (cfgFile, logfile)
-    cmd = HDRTool + args
-    ExecuteCmd(cmd, LogCmdOnly)
-
-def HDRTool_GatherQualityMetrics(recfile, logfilepath):
-    logfile = GetHDRLogFile(recfile, logfilepath)
-    results = ParseHDRToolLogFile(logfile)
-    return results
diff --git a/tools/convexhull_framework/src/CalcQtyWithVmafTool.py b/tools/convexhull_framework/src/CalcQtyWithVmafTool.py
index 46efc1e..c38211a 100644
--- a/tools/convexhull_framework/src/CalcQtyWithVmafTool.py
+++ b/tools/convexhull_framework/src/CalcQtyWithVmafTool.py
@@ -13,7 +13,7 @@
 import os
 import re
 import logging
-from Config import BinPath, LoggerName, LogCmdOnly, VMAF
+from Config import BinPath, LoggerName, VMAF
 from Utils import GetShortContentName, ExecuteCmd
 
 subloggername = "CalcQtyMetrics_VMAFTool"
@@ -21,24 +21,43 @@
 logger = logging.getLogger(loggername)
 
 Model_Pkg_File = os.path.join(BinPath, 'vmaf_v0.6.1.pkl')
-VMAFMetricsFullList = ['VMAF_Y', 'PSNR_Y', 'SSIM_Y', 'MS-SSIM_Y']
+VMAFMetricsFullList = ['VMAF_Y', 'PSNR_Y', 'PSNR_U', 'PSNR_V', 'SSIM_Y', 'MS-SSIM_Y']
 
 def ParseVMAFLogFile(vmaf_log):
     floats = len(VMAFMetricsFullList) * [0.0]
     flog = open(vmaf_log, 'r')
     for line in flog:
+        '''
         if 'aggregateVMAF' in line:
-            item = re.findall(r"aggregateVMAF=\"(\d+\.?\d*)\"", line)
+            item = re.findall(r"aggregateVMAF=\"([+|-]?\d+\.?\d*)\"", line)
             floats[0] = 0 if len(item) == 0 else item[0]
-            item = re.findall(r"aggregatePSNR=\"(\d+\.?\d*)\"", line)
+            item = re.findall(r"aggregatePSNR=\"([+|-]?\d+\.?\d*)\"", line)
             floats[1] = 0 if len(item) == 0 else item[0]
-            item = re.findall(r"aggregateSSIM=\"(\d+\.?\d*)\"", line)
+            item = re.findall(r"aggregateSSIM=\"([+|-]?\d+\.?\d*)\"", line)
             floats[2] = 0 if len(item) == 0 else item[0]
-            item = re.findall(r"aggregateMS_SSIM=\"(\d+\.?\d*)\"", line)
+            item = re.findall(r"aggregateMS_SSIM=\"([+|-]?\d+\.?\d*)\"", line)
             floats[3] = 0 if len(item) == 0 else item[0]
             break
+        '''
+        m = re.search(r"\"vmaf\".*mean=\"(\d+\.?\d*)\"\s+",line)
+        if m:
+            floats[0] = m.group(1)
+        m = re.search(r"\"psnr_y\".*mean=\"(\d+\.?\d*)\"\s+", line)
+        if m:
+            floats[1] = m.group(1)
+        m = re.search(r"\"psnr_cb\".*mean=\"(\d+\.?\d*)\"\s+", line)
+        if m:
+            floats[2] = m.group(1)
+        m = re.search(r"\"psnr_cr\".*mean=\"(\d+\.?\d*)\"\s+", line)
+        if m:
+            floats[3] = m.group(1)
+        m = re.search(r"\"float_ssim\".*mean=\"(\d+\.?\d*)\"\s+", line)
+        if m:
+            floats[4] = m.group(1)
+        m = re.search(r"\"float_ms_ssim\".*mean=\"(\d+\.?\d*)\"\s+", line)
+        if m:
+            floats[5] = m.group(1)
     flog.close()
-
     floats = [float(i) for i in floats]
 
     print_str = "VMAF quality metrics: "
@@ -56,10 +75,13 @@
 
 ################################################################################
 ##################### Exposed Functions ########################################
-def VMAF_CalQualityMetrics(origfile, recfile, num, w, h, logfilePath):
+def VMAF_CalQualityMetrics(origfile, recfile, fmt, num, w, h, bit_depth,
+                           logfilePath, LogCmdOnly=False):
     vmaf_log = GetVMAFLogFile(recfile, logfilePath)
-    args = " yuv420p %d %d %s %s %s --log %s --psnr --ssim --ms-ssim "\
-           % (w, h, origfile, recfile, Model_Pkg_File, vmaf_log)
+    args = " -r %s -d %s --feature psnr --feature float_ssim " \
+           " --feature float_ms_ssim -q --xml" \
+           " --model path=%s:name=vmaf -o %s"\
+           % (origfile, recfile, Model_Pkg_File, vmaf_log)
     cmd = VMAF + args
     ExecuteCmd(cmd, LogCmdOnly)
 
diff --git a/tools/convexhull_framework/src/CalculateQualityMetrics.py b/tools/convexhull_framework/src/CalculateQualityMetrics.py
index 4f61fa2..cdf99a2 100644
--- a/tools/convexhull_framework/src/CalculateQualityMetrics.py
+++ b/tools/convexhull_framework/src/CalculateQualityMetrics.py
@@ -11,63 +11,30 @@
 __author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
 
 import logging
-from Config import QualityList, LoggerName, QualityEvalMethods
+from Config import QualityList, LoggerName
 import Utils
 from CalcQtyWithVmafTool import VMAF_CalQualityMetrics, VMAF_GatherQualityMetrics,\
      VMAFMetricsFullList
-from CalcQtyWithHdrTools import HDRTool_CalQualityMetrics, HDRToolsMetricsFullList,\
-     HDRTool_GatherQualityMetrics
-from CalcQtyWithFfmpeg import FFMPEG_GatherQualityMetrics, FFMPEGMetricsFullList,\
-     FFMPEG_CalQualityMetrics
 
 subloggername = "CalcQtyMetrics"
 loggername = LoggerName + '.' + '%s' % subloggername
 logger = logging.getLogger(loggername)
 
-MetricsFullDict = {'VMAF': VMAFMetricsFullList, 'HDRTools': HDRToolsMetricsFullList,
-                   'FFMPEG': FFMPEGMetricsFullList}
-
-def CalculateQualityMetric(content, framenum, reconYUV, width, height, logfilePath,
-                           cfgfilePath):
+def CalculateQualityMetric(src_file, framenum, reconYUV, fmt, width, height,
+                           bit_depth, logfilePath, LogCmdOnly=False):
     Utils.CmdLogger.write("::Quality Metrics\n")
-
-    methods_torun = list(set(QualityEvalMethods))  # remove duplicate items
-    for method in methods_torun:
-        if method == 'VMAF':
-            VMAF_CalQualityMetrics(content, reconYUV, framenum, width, height,
-                                   logfilePath)
-        elif method == 'HDRTools':
-            HDRTool_CalQualityMetrics(content, reconYUV, framenum, width, height,
-                                      logfilePath, cfgfilePath)
-        elif method == 'FFMPEG':
-            FFMPEG_CalQualityMetrics(content, reconYUV, framenum, width, height,
-                                     logfilePath)
-        else:
-            logger.error("invalid quality evaluation method: %s !" % method)
-            return
+    VMAF_CalQualityMetrics(src_file, reconYUV, fmt, framenum, width, height,
+                           bit_depth, logfilePath, LogCmdOnly)
 
 def GatherQualityMetrics(reconYUV, logfilePath):
-    methods_torun = list(set(QualityEvalMethods))  # remove duplicate items
-    qresult_dict = {}
-    for method in methods_torun:
-        if method == 'VMAF':
-            qresult_dict[method] = VMAF_GatherQualityMetrics(reconYUV, logfilePath)
-        elif method == 'HDRTools':
-            qresult_dict[method] = HDRTool_GatherQualityMetrics(reconYUV, logfilePath)
-        elif method == 'FFMPEG':
-            qresult_dict[method] = FFMPEG_GatherQualityMetrics(reconYUV, logfilePath)
-        else:
-            logger.error("invalid quality evaluation method: %s !" % method)
-            return
-
+    qresult = VMAF_GatherQualityMetrics(reconYUV, logfilePath)
     results = []
-    for metric, method in zip(QualityList, QualityEvalMethods):
-        mfullList = MetricsFullDict[method]
-        if metric in mfullList:
-            indx = mfullList.index(metric)
-            results.append(qresult_dict[method][indx])
+    for metric in QualityList:
+        if metric in VMAFMetricsFullList:
+            indx = VMAFMetricsFullList.index(metric)
+            results.append(qresult[indx])
         else:
             logger.error("invalid quality metrics in QualityList")
-            results.append('none')
+            results.append(0.0)
 
     return results
diff --git a/tools/convexhull_framework/src/Config.py b/tools/convexhull_framework/src/Config.py
index 2fc154c..3ad8b06 100644
--- a/tools/convexhull_framework/src/Config.py
+++ b/tools/convexhull_framework/src/Config.py
@@ -11,6 +11,10 @@
 __author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
 
 import os
+import AV2CTCVideo
+
+#TEST_CONFIGURATIONS = ["RA","LD", "AS"]
+TEST_CONFIGURATIONS = ["RA"]
 
 ######################################
 # configuration settings
@@ -19,74 +23,16 @@
 BinPath = os.path.join(RootPath, 'bin')
 WorkPath = os.path.join(RootPath, 'test')
 SMOKE_TEST = False  # override some parameters to do a quick smoke test
-FrameNum = 10
+FrameNum = 2
 
 if SMOKE_TEST:
     FrameNum = 2
 
 ############ test contents #######################################
-ContentPath = "..\\video"
-# when Have_Class_Subfolder is set to True, test clips are put in subfolders
-# under ContentPath, each subfolder name represents a class. Class name in the
-# table of Clips is the subfolder name
-Have_Class_Subfolder = False
-Clips = {
-#    basename           class      width   height  framerate   bitdepth    fmt
-    #"CrowdRun":         ["ClassB",  1920,   1080,   30,     8,      "yuv420p"],
-    #"BasketballDrive":  ["ClassB",  1920,   1080,   30,     8,      "yuv420p"],
-    #"NetflixCrosswalk_1920x1080_60fps_8bit_420_60f":    ["ClassB",  1920,   1080,
-    #30,         8,          "yuv420p"],
-    "CrowdRun_1920x1080":    ["ClassB",  1920,   1080,   30,  8,    "yuv420p"],
-}
-'''
-    "aspen_1080p_60f":  ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "dark720p_60f":     ["ClassB",  1280,   720,    30,         8,
-              "yuv420p"],
-    "DOTA2_60f_420":    ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "duckstakeoff_1080p50_60f":     ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "KristenAndSara_1280x720_60f":  ["ClassB",  1280,   720,    30,         8,
-              "yuv420p"],
-    "life_1080p30_60f":             ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "MINECRAFT_60f_420":            ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "NetflixAerial_1920x1080_60fps_8bit_420_60f":       ["ClassB",  1920,   1080,
-       30,         8,          "yuv420p"],
-    "NetflixBoat_1920x1080_60fps_8bit_420_60f":         ["ClassB",  1920,   1080,
-       30,         8,          "yuv420p"],
-    "NetflixCrosswalk_1920x1080_60fps_8bit_420_60f":    ["ClassB",  1920,   1080,
-       30,         8,          "yuv420p"],
-    "NetflixDrivingPOV_1280x720_60fps_8bit_420_60f":    ["ClassB",  1280,   720,
-       30,         8,          "yuv420p"],
-    "NetflixFoodMarket_1920x1080_60fps_8bit_420_60f":   ["ClassB",  1920,   1080,
-       30,         8,          "yuv420p"],
-    "NetflixPierSeaside_1920x1080_60fps_8bit_420_60f":  ["ClassB",  1920,   1080,
-       30,         8,          "yuv420p"],
-    "NetflixRollerCoaster_1280x720_60fps_8bit_420_60f": ["ClassB",  1280,   720,
-        30,         8,          "yuv420p"],
-    "NetflixSquareAndTimelapse_1920x1080_60fps_8bit_420_60f":   ["ClassB",  1920,
-       1080,   30,         8,          "yuv420p"],
-    "NetflixTunnelFlag_1920x1080_60fps_8bit_420_60f":           ["ClassB",  1920,
-       1080,   30,         8,          "yuv420p"],
-    "rushhour_1080p25_60f": ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "STARCRAFT_60f_420":    ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "touchdownpass_1080p_60f":  ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-    "vidyo1_720p_60fps_60f":    ["ClassB",  1280,   720,    30,         8,
-              "yuv420p"],
-    "vidyo4_720p_60fps_60f":    ["ClassB",  1280,   720,    30,         8,
-              "yuv420p"],
-    "wikipedia_420":            ["ClassB",  1920,   1080,   30,         8,
-              "yuv420p"],
-'''
+ContentPath = "D:\\YUVs\\AV2-CTC"
 ############## Scaling settings ############################################
 # down scaling ratio
-DnScaleRatio = [1.0, 1.5, 2.0, 2.5, 3.0, 4.0] #, 6.0]  # downscale ratio
+DnScaleRatio = [1.0, 1.5, 2.0, 3.0, 4.0, 6.0]  # downscale ratio
 #down and up scaling algorithm, the 2 lists should be of same size
 DnScalingAlgos = ['lanczos'] #['bicubic', 'bilinear', 'gauss', 'lanczos', 'sinc']
 UpScalingAlgos = ['lanczos'] #['bicubic', 'bilinear', 'gauss', 'lanczos', 'sinc']
@@ -94,29 +40,22 @@
 if SMOKE_TEST:
     DnScalingAlgos = ['bicubic', 'lanczos', 'sinc']
     UpScalingAlgos = ['bicubic', 'lanczos', 'sinc']
+HDRToolsConfigFileTemplate = os.path.join(BinPath, 'HDRConvScalerY4MFile.cfg')
+HDRConvert = os.path.join(BinPath, 'HDRConvert.exe')
 
 ##################### Encode Config ########################################
-EncodeMethods = ["ffmpeg", "aom", "svt"]
-CodecNames = ["hevc", "av1"]
-SUFFIX = {"hevc": ".265", "av1": ".ivf"}
+EncodeMethods = ["aom", "svt"]
+CodecNames = ["av1"]
+SUFFIX = {"av1": ".ivf"}
 FFMPEG = os.path.join(BinPath, 'ffmpeg.exe')
 AOMENC = os.path.join(BinPath, 'aomenc.exe')
 SVTAV1 = os.path.join(BinPath, 'SvtAv1EncApp.exe')
-SVTHEVC = os.path.join(BinPath, 'SvtHevcEncApp.exe')
 AOMDEC = os.path.join(BinPath, 'aomdec.exe')
-
-QPs = list(range(22, 51, 5))  # total 6 QPs
-if SMOKE_TEST:
-    QPs = list(range(12, 45, 10))  # total 4 QPs
-
+QPs = [23, 31, 39, 47, 55, 63]
 ######################## quality evalution config #############################
 QualityList = ['VMAF_Y', 'PSNR_Y', 'PSNR_U', 'PSNR_V', 'SSIM_Y', 'MS-SSIM_Y']
-# method should be one of 'VMAF', 'FFMPEG' or 'HDRTools'
-QualityEvalMethods = ['VMAF', 'HDRTools', 'HDRTools', 'HDRTools', 'HDRTools',
-                      'HDRTools']
-VMAF = os.path.join(BinPath, 'vmafossexec.exe')
-HDRTool = os.path.join(BinPath, 'HDRMetrics.exe')
-CalcBDRateInExcel = False
+VMAF = os.path.join(BinPath, 'vmaf_rc.exe')
+CalcBDRateInExcel = True
 EnablePreInterpolation = True
 
 ######################## config for exporting data to excel  #################
@@ -166,6 +105,5 @@
                       len(QualityList)) * i + ScalQty_startCol + 1
                      for i in range(len(DnScalingAlgos))]
 ######################## logging #########################################
-LoggerName = "ConvexHullTest"
+LoggerName = "AV2CTC"
 LogLevels = ['NONE', 'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']
-LogCmdOnly = False
diff --git a/tools/convexhull_framework/src/ConvexHullTest.py b/tools/convexhull_framework/src/ConvexHullTest.py
index 8493db1..80e131d 100644
--- a/tools/convexhull_framework/src/ConvexHullTest.py
+++ b/tools/convexhull_framework/src/ConvexHullTest.py
@@ -20,21 +20,21 @@
 from EncDecUpscale import Run_EncDec_Upscale, GetBsReconFileName
 from VideoScaler import GetDownScaledOutFile, DownScaling
 from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
-from Utils import GetShortContentName, GetVideoInfo, CreateChart_Scatter,\
+from Utils import GetShortContentName, CreateChart_Scatter,\
      AddSeriesToChart_Scatter, InsertChartsToSheet, CreateNewSubfolder,\
-     GetContents, SetupLogging, UpdateChart, AddSeriesToChart_Scatter_Rows,\
-     Cleanfolder
-from PostAnalysis_Summary import GenerateSummaryRDDataExcelFile,\
-     GenerateSummaryConvexHullExcelFile
+     SetupLogging, UpdateChart, AddSeriesToChart_Scatter_Rows,\
+     Cleanfolder, CreateClipList, Clip
+from PostAnalysis_Summary import GenerateSumRDExcelFile,\
+     GenerateSumCvxHullExcelFile
 from ScalingTest import Run_Scaling_Test, SaveScalingResultsToExcel
 import Utils
-from Config import LogLevels, FrameNum, DnScaleRatio, QPs, CvxH_WtCols,\
-     CvxH_WtRows, QualityList, LineColors, DnScalingAlgos, UpScalingAlgos,\
-     ContentPath, SummaryOutPath, WorkPath, Path_RDResults, Clips, \
-     ConvexHullColor, EncodeMethods, CodecNames, LoggerName, LogCmdOnly, \
-     TargetQtyMetrics, CvxHDataRows, CvxHDataStartRow, CvxHDataStartCol, \
-     CvxHDataNum, Int_ConvexHullColor, EnablePreInterpolation
 from operator import itemgetter
+from Config import LogLevels, FrameNum, QPs, CvxH_WtCols,\
+     CvxH_WtRows, QualityList, LineColors, SummaryOutPath, WorkPath, \
+     Path_RDResults, DnScalingAlgos, UpScalingAlgos, ConvexHullColor, \
+     EncodeMethods, CodecNames, LoggerName, DnScaleRatio, TargetQtyMetrics, \
+     CvxHDataRows, CvxHDataStartRow, CvxHDataStartCol, CvxHDataNum, \
+     Int_ConvexHullColor, EnablePreInterpolation
 
 ###############################################################################
 ##### Helper Functions ########################################################
@@ -46,16 +46,16 @@
     for folder in folders:
         Cleanfolder(folder)
 
-def GetRDResultExcelFile(content):
-    contentBaseName = GetShortContentName(content)
+def GetRDResultExcelFile(clip):
+    contentBaseName = GetShortContentName(clip.file_name, False)
     filename = "RDResults_%s_%s_%s_%s.xlsx" % (contentBaseName, EncodeMethod,
                                                CodecName, EncodePreset)
     file = os.path.join(Path_RDResults, filename)
     return file
 
 def setupWorkFolderStructure():
-    global Path_Bitstreams, Path_DecodedYuv, Path_UpScaleYuv, Path_DnScaleYuv,\
-           Path_QualityLog, Path_TestLog, Path_CfgFiles, Path_DecUpScaleYuv
+    global Path_Bitstreams, Path_DecodedYuv, Path_UpScaleYuv, Path_DnScaleYuv, \
+    Path_QualityLog, Path_TestLog, Path_CfgFiles, Path_DecUpScaleYuv
     Path_Bitstreams = CreateNewSubfolder(WorkPath, "bistreams")
     Path_DecodedYuv = CreateNewSubfolder(WorkPath, "decodedYUVs")
     Path_UpScaleYuv = CreateNewSubfolder(WorkPath, "upscaledYUVs")
@@ -112,13 +112,15 @@
     return lower, upper
 
 
-def LookUpQPAndResolutionInConvexHull(qtyvals, qtyhull, qtycvhQPs, qtycvhRes):
+def LookUpQPAndResInCvxHull(qtyvals, qtyhull, qtycvhQPs, qtycvhRes):
     cvhqtys = [h[1] for h in qtyhull]
     qtyQPs = []; qtyRes = []
     for val in qtyvals:
         closest_idx = min(range(len(cvhqtys)), key=lambda i: abs(cvhqtys[i] - val))
-        if (closest_idx == 0 and val > cvhqtys[0]) or (closest_idx == (len(qtyvals) - 1) and val < cvhqtys[-1]):
-            Utils.Logger.info("the give value of quality metric is out of range of convex hull test quality values.")
+        if (closest_idx == 0 and val > cvhqtys[0]) or \
+           (closest_idx == (len(qtyvals) - 1) and val < cvhqtys[-1]):
+            Utils.Logger.info("the give value of quality metric is out of range"\
+                              "of convex hull test quality values.")
 
         qtyQPs.append(qtycvhQPs[closest_idx])
         qtyRes.append(qtycvhRes[closest_idx])
@@ -180,18 +182,22 @@
 
     # find out QP/resolution for given qty metric and qty value
     startrow_fdout = endrow + 1
-    sht.write(startrow_fdout, CvxHDataStartCol, "  Find out QP/resolution for given quality metrics:")
+    sht.write(startrow_fdout, CvxHDataStartCol,
+              "  Find out QP/resolution for given quality metrics:")
     numitem_fdout = 4  # qtymetric values, QP, resolution, one empty row
-    startrows_fdout = [startrow_fdout + 1 + i * numitem_fdout for i in range(len(tgtqmetrics))]
+    startrows_fdout = [startrow_fdout + 1 + i * numitem_fdout
+                       for i in range(len(tgtqmetrics))]
 
     for metric, idx in zip(tgtqmetrics, range(len(tgtqmetrics))):
         if metric not in QualityList:
-            Utils.Logger.error("wrong qty metric name. should be one of the name in QualityList.")
+            Utils.Logger.error("wrong qty metric name. should be one of the" \
+                               " name in QualityList.")
             return endrow
 
         qtyvals = tgtqmetrics[metric]
-        qtyQPs, qtyRes = LookUpQPAndResolutionInConvexHull(qtyvals, hull[metric],
-                                                       cvh_QPs[metric], cvh_Res_txt[metric])
+        qtyQPs, qtyRes = LookUpQPAndResInCvxHull(qtyvals, hull[metric],
+                                                 cvh_QPs[metric],
+                                                 cvh_Res_txt[metric])
         # write the look up result into excel file
         startrow = startrows_fdout[idx]
         sht.write(startrow, CvxHDataStartCol, metric)
@@ -215,46 +221,40 @@
     for folder in folders:
         Cleanfolder(folder)
 
-def Run_ConvexHull_Test(content, dnScalAlgo, upScalAlgo):
-    Utils.Logger.info("%s %s start running xcode tests with %s" %
-                      (EncodeMethod, CodecName, os.path.basename(content)))
-    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
-    if totalnum < FrameNum:
-        Utils.Logger.error("content %s includes total %d frames < specified % frames!"
-                           % (content, totalnum, FrameNum))
-        return
-
-    DnScaledRes = [(int(width / ratio), int(height / ratio)) for ratio in DnScaleRatio]
+def Run_ConvexHull_Test(clip, dnScalAlgo, upScalAlgo, LogCmdOnly = False):
+    Utils.Logger.info("start encode %s" % clip.file_name)
+    DnScaledRes = [(int(clip.width / ratio), int(clip.height / ratio)) for ratio in
+                   DnScaleRatio]
     for i in range(len(DnScaledRes)):
         if SaveMemory:
             CleanIntermediateFiles()
         DnScaledW = DnScaledRes[i][0]
         DnScaledH = DnScaledRes[i][1]
-        #downscaling if the downscaled file does not exist
-        dnscalyuv = GetDownScaledOutFile(content, width, height, DnScaledW,
-                                         DnScaledH, Path_DnScaleYuv, dnScalAlgo)
+        # downscaling if the downscaled file does not exist
+        dnscalyuv = GetDownScaledOutFile(clip, DnScaledW, DnScaledH,
+                                         Path_DnScaleYuv, dnScalAlgo)
         if not os.path.isfile(dnscalyuv):
-            dnscalyuv = DownScaling(content, FrameNum, width, height, DnScaledW,
-                                    DnScaledH, Path_DnScaleYuv, dnScalAlgo)
-
+            dnscalyuv = DownScaling(clip, FrameNum, DnScaledW, DnScaledH,
+                                    Path_DnScaleYuv, dnScalAlgo, LogCmdOnly)
+        ds_clip = Clip(GetShortContentName(dnscalyuv, False)+'.y4m', dnscalyuv,
+                       "", DnScaledW, DnScaledH, clip.fmt, clip.fps_num,
+                       clip.fps_denom, clip.bit_depth)
         for QP in QPs:
-            Utils.Logger.info("start transcode and upscale for %dx%d and QP %d"
-                              % (DnScaledW, DnScaledH, QP))
-            #transcode and upscaling
+            Utils.Logger.info("start encode and upscale for QP %d" % QP)
+            #encode and upscaling
             reconyuv = Run_EncDec_Upscale(EncodeMethod, CodecName, EncodePreset,
-                                          dnscalyuv, QP, FrameNum, fr, DnScaledW,
-                                          DnScaledH, width, height, Path_Bitstreams,
+                                          ds_clip, 'AS', QP, FrameNum, clip.width,
+                                          clip.height, Path_Bitstreams,
                                           Path_DecodedYuv, Path_DecUpScaleYuv,
-                                          upScalAlgo)
+                                          Path_CfgFiles, upScalAlgo, LogCmdOnly)
             #calcualte quality distortion
-            Utils.Logger.info("start quality metric calculation for %dx%d and QP %d"
-                              % (DnScaledW, DnScaledH, QP))
-            CalculateQualityMetric(content, FrameNum, reconyuv, width, height,
-                                   Path_QualityLog, Path_CfgFiles)
-
-    if SaveMemory:
-        Cleanfolder(Path_DnScaleYuv)
-
+            Utils.Logger.info("start quality metric calculation")
+            CalculateQualityMetric(clip.file_path, FrameNum, reconyuv,
+                                   clip.fmt, clip.width, clip.height,
+                                   clip.bit_depth, Path_QualityLog, LogCmdOnly)
+        if SaveMemory:
+            Cleanfolder(Path_DnScaleYuv)
+        Utils.Logger.info("finish running encode test.")
     Utils.Logger.info("finish running encode test.")
 
 def Interpolate(RDPoints):
@@ -294,16 +294,16 @@
     Utils.Logger.info("start saving RD results to excel file.......")
     if not os.path.exists(Path_RDResults):
         os.makedirs(Path_RDResults)
-    excFile = GetRDResultExcelFile(content)
+    excFile = GetRDResultExcelFile(clip)
     wb = xlsxwriter.Workbook(excFile)
     shts = []
     for i in range(len(dnScAlgos)):
         shtname = dnScAlgos[i] + '--' + upScAlgos[i]
         shts.append(wb.add_worksheet(shtname))
 
-    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
-    DnScaledRes = [(int(width / ratio), int(height / ratio)) for ratio in DnScaleRatio]
-    contentname = GetShortContentName(content)
+    DnScaledRes = [(int(clip.width / ratio), int(clip.height / ratio))
+                   for ratio in DnScaleRatio]
+    contentname = GetShortContentName(clip.file_name)
     for sht, indx in zip(shts, list(range(len(dnScAlgos)))):
         # write QP
         sht.write(1, 0, "QP")
@@ -328,12 +328,13 @@
 
             bitratesKbps = []; qualities = []
             for qp in QPs:
-                bs, reconyuv = GetBsReconFileName(EncodeMethod, CodecName,
-                                                  EncodePreset, content, width,
-                                                  height, DnScaledW, DnScaledH,
-                                                  dnScAlgos[indx], upScAlgos[indx],
-                                                  qp, Path_Bitstreams)
-                bitrate = (os.path.getsize(bs) * 8 * fr / FrameNum) / 1000.0
+                bs, reconyuv = GetBsReconFileName(EncodeMethod, CodecName, 'AS',
+                                                  EncodePreset, clip, DnScaledW,
+                                                  DnScaledH, dnScAlgos[indx],
+                                                  upScAlgos[indx], qp,
+                                                  Path_Bitstreams)
+                bitrate = (os.path.getsize(bs) * 8 * (clip.fps_num / clip.fps_denom)
+                           / FrameNum) / 1000.0
                 bitratesKbps.append(bitrate)
                 quality = GatherQualityMetrics(reconyuv, Path_QualityLog)
                 qualities.append(quality)
@@ -397,26 +398,29 @@
                         help="save memory mode will delete most files in"
                              " intermediate steps and keeps only necessary "
                              "ones for RD calculation. It is false by default")
+    parser.add_argument('-CmdOnly', "--LogCmdOnly", dest='LogCmdOnly', type=bool,
+                        default=False, metavar='',
+                        help="LogCmdOnly mode will only capture the command sequences"
+                             "It is false by default")
     parser.add_argument('-l', "--LoggingLevel", dest='LogLevel', type=int,
                         default=3, choices=range(len(LogLevels)), metavar='',
                         help="logging level: 0:No Logging, 1: Critical, 2: Error,"
                              " 3: Warning, 4: Info, 5: Debug")
     parser.add_argument('-c', "--CodecName", dest='CodecName', type=str,
                         choices=CodecNames, metavar='',
-                        help="CodecName: av1 or hevc")
+                        help="CodecName: av1")
     parser.add_argument('-m', "--EncodeMethod", dest='EncodeMethod', type=str,
                         choices=EncodeMethods, metavar='',
-                        help="EncodeMethod: ffmpeg, aom, svt")
+                        help="EncodeMethod: aom, svt")
     parser.add_argument('-p', "--EncodePreset", dest='EncodePreset', type=str,
-                        metavar='', help="EncodePreset: medium, slow, fast, etc"
-                                         " for ffmpeg, 0,1,2... for aom and svt")
+                        metavar='', help="EncodePreset: 0,1,2... for aom and svt")
     if len(raw_args) == 1:
         parser.print_help()
         sys.exit(1)
     args = parser.parse_args(raw_args[1:])
 
     global Function, KeepUpscaledOutput, SaveMemory, LogLevel, CodecName,\
-        EncodeMethod, EncodePreset
+        EncodeMethod, EncodePreset, LogCmdOnly
     Function = args.Function
     KeepUpscaledOutput = args.KeepUpscaledOutput
     SaveMemory = args.SaveMemory
@@ -424,15 +428,17 @@
     CodecName = args.CodecName
     EncodeMethod = args.EncodeMethod
     EncodePreset = args.EncodePreset
+    LogCmdOnly = args.LogCmdOnly
 
 
 ######################################
 # main
 ######################################
 if __name__ == "__main__":
+    #sys.argv = ["","-f","clean"]
     #sys.argv = ["","-f","scaling"]
     #sys.argv = ["", "-f", "sumscaling"]
-    #sys.argv = ["", "-f", "encode","-c","av1","-m","aom","-p","1"]
+    #sys.argv = ["", "-f", "encode","-c","av1","-m","aom","-p","6"]
     #sys.argv = ["", "-f", "convexhull","-c","av1","-m","aom","-p","6"]
     #sys.argv = ["", "-f", "summary", "-c", "av1", "-m", "aom", "-p", "6"]
     ParseArguments(sys.argv)
@@ -441,41 +447,43 @@
     setupWorkFolderStructure()
     if Function != 'clean':
         SetupLogging(LogLevel, LogCmdOnly, LoggerName, Path_TestLog)
-        Contents = GetContents(ContentPath, Clips)
+        clip_list = CreateClipList('AS')
 
     # execute functions
     if Function == 'clean':
         CleanUp_workfolders()
     elif Function == 'scaling':
-        for content in Contents:
+        for clip in clip_list:
             for dnScaleAlgo, upScaleAlgo in zip(DnScalingAlgos, UpScalingAlgos):
-                Run_Scaling_Test(content, dnScaleAlgo, upScaleAlgo,
+                Run_Scaling_Test(clip, dnScaleAlgo, upScaleAlgo,
                                  Path_DnScaleYuv, Path_UpScaleYuv, Path_QualityLog,
-                                 Path_CfgFiles, SaveMemory, KeepUpscaledOutput)
+                                 Path_CfgFiles, SaveMemory, KeepUpscaledOutput,
+                                 LogCmdOnly)
     elif Function == 'sumscaling':
-        SaveScalingResultsToExcel(DnScalingAlgos, UpScalingAlgos, Path_QualityLog)
+        SaveScalingResultsToExcel(DnScalingAlgos, UpScalingAlgos, clip_list,
+                                  Path_QualityLog)
     elif Function == 'encode':
-        for content in Contents:
+        for clip in clip_list:
             for dnScalAlgo, upScalAlgo in zip(DnScalingAlgos, UpScalingAlgos):
-                Run_ConvexHull_Test(content, dnScalAlgo, upScalAlgo)
+                Run_ConvexHull_Test(clip, dnScalAlgo, upScalAlgo, LogCmdOnly)
     elif Function == 'convexhull':
-        for content in Contents:
-            SaveConvexHullResultsToExcel(content, DnScalingAlgos, UpScalingAlgos,
+        for clip in clip_list:
+            SaveConvexHullResultsToExcel(clip, DnScalingAlgos, UpScalingAlgos,
                                          EnablePreInterpolation)
     elif Function == 'summary':
         RDResultFilesGenerated = []
-        for content in Contents:
-            RDResultFilesGenerated.append(GetRDResultExcelFile(content))
+        for clip in clip_list:
+            RDResultFilesGenerated.append(GetRDResultExcelFile(clip))
 
-        RDsmfile = GenerateSummaryRDDataExcelFile(EncodeMethod, CodecName, EncodePreset,
-                                                  SummaryOutPath, RDResultFilesGenerated,
-                                                  ContentPath, Clips)
+        RDsmfile = GenerateSumRDExcelFile(EncodeMethod, CodecName, EncodePreset,
+                                          SummaryOutPath, RDResultFilesGenerated,
+                                          clip_list)
         Utils.Logger.info("RD data summary file generated: %s" % RDsmfile)
 
-        CvxHsmfile = GenerateSummaryConvexHullExcelFile(EncodeMethod, CodecName,
-                                                        EncodePreset, SummaryOutPath,
-                                                        RDResultFilesGenerated,
-                                                        EnablePreInterpolation)
+        CvxHsmfile = GenerateSumCvxHullExcelFile(EncodeMethod, CodecName,
+                                                 EncodePreset, SummaryOutPath,
+                                                 RDResultFilesGenerated,
+                                                 EnablePreInterpolation)
         Utils.Logger.info("Convel hull summary file generated: %s" % CvxHsmfile)
     else:
         Utils.Logger.error("invalid parameter value of Function")
diff --git a/tools/convexhull_framework/src/EncDecUpscale.py b/tools/convexhull_framework/src/EncDecUpscale.py
index 812eade..66d623a 100644
--- a/tools/convexhull_framework/src/EncDecUpscale.py
+++ b/tools/convexhull_framework/src/EncDecUpscale.py
@@ -15,7 +15,7 @@
 from VideoDecoder import VideoDecode
 from VideoScaler import UpScaling, GetDownScaledOutFile, GetUpScaledOutFile
 from Config import SUFFIX, LoggerName
-from Utils import GetShortContentName
+from Utils import GetShortContentName, Clip
 import logging
 
 subloggername = "EncDecUpscale"
@@ -24,62 +24,67 @@
 
 ################################################################################
 ##################### Internal Helper Functions ################################
-def GetBitstreamFile(method, codec, preset, yuvfile, qp, outpath):
+def GetBitstreamFile(method, codec, test_cfg, preset, yuvfile, qp, outpath):
     bs_suffix = SUFFIX[codec]
-    Prefix_EncodeCfg = '_%s_%s_Preset_%s' % (method, codec, preset)
+    Prefix_EncodeCfg = '_%s_%s_%s_Preset_%s' % (method, codec, test_cfg, preset)
     filename = GetShortContentName(yuvfile, False) + Prefix_EncodeCfg + "_QP_"\
                + str(qp) + bs_suffix
     filename = os.path.join(outpath, filename)
     return filename
 
 def GetDecodedFile(bsfile, outpath):
-    filename = GetShortContentName(bsfile, False) + '_Decoded.yuv'
+    filename = GetShortContentName(bsfile, False) + '_Decoded.y4m'
     decodedfile = os.path.join(outpath, filename)
     return decodedfile
 
 ################################################################################
 ##################### Major Functions ##########################################
-def Encode(method, codec, preset, input, qp, num, framerate, width, height, path):
-    bsfile = GetBitstreamFile(method, codec, preset, input, qp, path)
+def Encode(method, codec, preset, clip, test_cfg, qp, num, path,
+           LogCmdOnly=False):
+    bsfile = GetBitstreamFile(method, codec, test_cfg, preset, clip.file_path,
+                              qp, path)
     # call VideoEncoder to do the encoding
-    VideoEncode(method, codec, input, qp, num, framerate, width, height, bsfile,
-                preset)
+    VideoEncode(method, codec, clip, test_cfg, qp, num, bsfile, preset,
+                LogCmdOnly)
     return bsfile
 
-
-def Decode(codec, bsfile, path):
+def Decode(codec, bsfile, path, LogCmdOnly=False):
     decodedfile = GetDecodedFile(bsfile, path)
     #call VideoDecoder to do the decoding
-    VideoDecode(codec, bsfile, decodedfile)
+    VideoDecode(codec, bsfile, decodedfile, LogCmdOnly)
     return decodedfile
 
-
-def Run_EncDec_Upscale(method, codec, preset, input, QP, num, framerate, inw,
-                       inh, outw, outh, path_bs, path_decoded,
-                       path_upscaled, algo):
-    logger.info("%s %s start encode file %s with QP = %d"
-                % (method, codec, os.path.basename(input), QP))
-    bsFile = Encode(method, codec, preset, input, QP, num, framerate, inw, inh,
-                    path_bs)
+def Run_EncDec_Upscale(method, codec, preset, clip, test_cfg, QP, num, outw,
+                       outh, path_bs, path_decoded, path_upscaled, path_cfg,
+                       upscale_algo, LogCmdOnly = False):
+    logger.info("%s %s start encode file %s with QP = %d" %
+                (method, codec, clip.file_name, QP))
+    bsFile = Encode(method, codec, preset, clip, test_cfg, QP, num, path_bs,
+                    LogCmdOnly)
     logger.info("start decode file %s" % os.path.basename(bsFile))
-    decodedYUV = Decode(codec, bsFile, path_decoded)
+    decodedYUV = Decode(codec, bsFile, path_decoded, LogCmdOnly)
     logger.info("start upscale file %s" % os.path.basename(decodedYUV))
-    upscaledYUV = UpScaling(decodedYUV, num, inw, inh, outw, outh, path_upscaled,
-                            algo)
+    dec_clip = Clip(GetShortContentName(decodedYUV, False) + '.y4m',
+                    decodedYUV, clip.file_class, clip.width, clip.height,
+                    clip.fmt, clip.fps_num, clip.fps_denom, clip.bit_depth)
+    upscaledYUV = UpScaling(dec_clip, num, outw, outh, path_upscaled, path_cfg,
+                            upscale_algo, LogCmdOnly)
     logger.info("finish Run Encode, Decode and Upscale")
     return upscaledYUV
 
 
-def GetBsReconFileName(encmethod, codecname, preset, content, width, height,
-                       dnwidth, dnheight, dnScAlgo,
-                       upScAlgo, qp, path_bs):
-    dsyuv_name = GetDownScaledOutFile(content, width, height, dnwidth, dnheight,
-                                      path_bs, dnScAlgo)
+def GetBsReconFileName(encmethod, codecname, test_cfg, preset, clip, dw, dh,
+                       dnScAlgo, upScAlgo, qp, path_bs):
+    dsyuv_name = GetDownScaledOutFile(clip, dw, dh, path_bs, dnScAlgo)
     # return bitstream file with absolute path
-    bs = GetBitstreamFile(encmethod, codecname, preset, dsyuv_name, qp, path_bs)
+    bs = GetBitstreamFile(encmethod, codecname, test_cfg, preset, dsyuv_name,
+                          qp, path_bs)
     decoded = GetDecodedFile(bs, path_bs)
-    reconfilename = GetUpScaledOutFile(decoded, dnwidth, dnheight, width, height,
-                                       path_bs, upScAlgo)
+    ds_clip = Clip(GetShortContentName(decoded, False) + '.y4m',
+                   decoded, clip.file_class, dw, dh, clip.fmt, clip.fps_num,
+                   clip.fps_denom, clip.bit_depth)
+    reconfilename = GetUpScaledOutFile(ds_clip, clip.width, clip.height,
+                                       upScAlgo, path_bs)
     # return only Recon yuv file name w/o path
-    reconfilename = GetShortContentName(reconfilename, False)
+    reconfilename = GetShortContentName(reconfilename, False) + '.y4m'
     return bs, reconfilename
diff --git a/tools/convexhull_framework/src/PostAnalysis_Summary.py b/tools/convexhull_framework/src/PostAnalysis_Summary.py
index 290c8c9..c686b48 100644
--- a/tools/convexhull_framework/src/PostAnalysis_Summary.py
+++ b/tools/convexhull_framework/src/PostAnalysis_Summary.py
@@ -79,11 +79,11 @@
 
     # copy the results data from each content's result file to corresponding
     # location in summary excel file
-    for (cls, contents), row_class in zip(ContentsDict.items(), Rows_Class):
+    for (cls, clip_list), row_class in zip(ClipDict.items(), Rows_Class):
         sht.write(row_class, 0, cls)
-        rows_content = [i * len(QPs) for i in range(len(contents))]
-        for content, row_cont in zip(contents, rows_content):
-            key = GetShortContentName(content)
+        rows_content = [i * len(QPs) for i in range(len(clip_list))]
+        for clip, row_cont in zip(clip_list, rows_content):
+            key = GetShortContentName(clip.file_name)
             sht.write(row_class + row_cont, 1, key)
             rdwb = None
             for resfile in resultfiles:
@@ -97,7 +97,7 @@
             assert rdwb is not None
             if rdwb is None:
                 logger.warning("not find convex hull result file for content:%s"
-                               % content)
+                               % clip.file_name)
 
 def CalBDRateWithExcel_OneSheet(sht, cols, cols_bdmtrs, cellformat):
     row_refst = 0
@@ -106,8 +106,8 @@
         sht.write(0, cols_bd, 'BD-Rate %.2f vs. %.2f' % (DnScaleRatio[residx],
                                                          DnScaleRatio[0]))
         sht.write_row(1, cols_bd, QualityList)
-        for (cls, contents), row_class in zip(ContentsDict.items(), Rows_Class):
-            rows_content = [i * len(QPs) for i in range(len(contents))]
+        for (cls, clip_list), row_class in zip(ClipDict.items(), Rows_Class):
+            rows_content = [i * len(QPs) for i in range(len(clip_list))]
             for row_cont in rows_content:
                 for y in range(len(QualityList)):
                     refbr_b = xlrd.cellnameabs(row_class + row_cont + row_refst,
@@ -150,10 +150,10 @@
         sht.write(0, cols_bd, 'BD-Rate %.2f vs. %.2f' % (DnScaleRatio[residx],
                                                          DnScaleRatio[0]))
         sht.write_row(1, cols_bd, QualityList)
-        for (cls, contents), row_class in zip(ContentsDict.items(), Rows_Class):
-            rows_content = [i * len(QPs) for i in range(len(contents))]
-            for row_cont, content in zip(rows_content, contents):
-                key = GetShortContentName(content)
+        for (cls, clip_list), row_class in zip(ClipDict.items(), Rows_Class):
+            rows_content = [i * len(QPs) for i in range(len(clip_list))]
+            for row_cont, clip in zip(rows_content, clip_list):
+                key = GetShortContentName(clip.file_name)
                 for resfile in resultfiles:
                     if key in resfile:
                         rdwb = xlrd.open_workbook(resfile)
@@ -214,16 +214,16 @@
 
     startrow = 3
     step = len(QPs)
-    rows_class_avg = [startrow + step * i for i in range(len(ContentsDict))]
+    rows_class_avg = [startrow + step * i for i in range(len(ClipDict))]
     totalnum_content = 0
-    for (cls, contents), row_class, rdclassrow in zip(ContentsDict.items(),
+    for (cls, clip_list), row_class, rdclassrow in zip(ClipDict.items(),
                                                       rows_class_avg,
                                                       Rows_Class):
         avg_sht.write(row_class, 0, cls)
-        totalnum_content = totalnum_content + len(contents)
-        avg_sht.write(row_class, 1, len(contents))
+        totalnum_content = totalnum_content + len(clip_list)
+        avg_sht.write(row_class, 1, len(clip_list))
         avg_sht.write_column(row_class, 2, QPs)
-        rows_content = [i * len(QPs) for i in range(len(contents))]
+        rows_content = [i * len(QPs) for i in range(len(clip_list))]
 
         for rdcol, col_res, residx in zip(rdcols, cols_res, range(len(DnScaleRatio))):
             for i in range(len(QPs)):
@@ -293,7 +293,7 @@
     cols_upscl_bd = [step_upscl * i for i in range(len(upScalAlgos))]
     step_res = len(upScalAlgos) * step_upscl + colintval_dnscalres
     cols_res_bd = [step_res * i + startcol for i in range(len(DnScaleRatio) - 1)]
-    rows_class_rdavg = [startrow + i for i in range(len(ContentsDict))]
+    rows_class_rdavg = [startrow + i for i in range(len(ClipDict))]
 
     for residx, col_res_bd in zip(range(1, len(DnScaleRatio)), cols_res_bd):
         bdavg_sht.write(0, col_res_bd, 'BD-Rate %.2f vs. %.2f'
@@ -303,13 +303,13 @@
             bdavg_sht.write_row(2, col_res_bd + col_upscl_bd, QualityList)
 
     totalnum_content = 0
-    for (cls, contents), row_class, rdclassrow in zip(ContentsDict.items(),
+    for (cls, clip_list), row_class, rdclassrow in zip(ClipDict.items(),
                                                       rows_class_rdavg,
                                                       Rows_Class):
         bdavg_sht.write(row_class, 0, cls)
-        totalnum_content = totalnum_content + len(contents)
-        bdavg_sht.write(row_class, 1, len(contents))
-        rows_content = [i * len(QPs) for i in range(len(contents))]
+        totalnum_content = totalnum_content + len(clip_list)
+        bdavg_sht.write(row_class, 1, len(clip_list))
+        rows_content = [i * len(QPs) for i in range(len(clip_list))]
         sum_rows = [rdclassrow + row_cont for row_cont in rows_content]
         for rdcol, col_res in zip(rd_cols_bdmtrs, cols_res_bd):
             # write average bd rate
@@ -350,8 +350,8 @@
 # resultfiles   is a list of all convex hull RD result files generated by
 #                runninging '-f convexhull'
 # summary_outpath  is the folder where output summary file will be
-def GenerateSummaryRDDataExcelFile(encMethod, codecName, preset, summary_outpath,
-                             resultfiles, content_path, clips):
+def GenerateSumRDExcelFile(encMethod, codecName, preset, summary_outpath,
+                           resultfiles, clip_list):
 
     global dnScalAlgos, upScalAlgos
     # find all scaling algos tested in results file,
@@ -377,10 +377,8 @@
     colInterval = 2
     rowstart = 2
     # to generate rows number of starting of each class: Rows_Class
-    global ContentsDict, Rows_Class
-    ContentsDict, Rows_Class = CalcRowsClassAndContentDict(rowstart,
-                                                           content_path,
-                                                           clips, len(QPs))
+    global ClipDict, Rows_Class
+    ClipDict, Rows_Class = CalcRowsClassAndContentDict(rowstart, clip_list, len(QPs))
     # cols is column number of results files
     step = colInterval + 1 + len(QualityList)  # 1 is for bitrate
     sum_wtcols = [step * i + colstart for i in range(len(DnScaleRatio))]
@@ -413,9 +411,8 @@
     wb.close()
     return smfile
 
-def GenerateSummaryConvexHullExcelFile(encMethod, codecName, preset,
-                                       summary_outpath, resultfiles,
-                                       EnablePreInterpolation = False):
+def GenerateSumCvxHullExcelFile(encMethod, codecName, preset, summary_outpath,
+                                resultfiles, EnablePreInterpolation = False):
     if not os.path.exists(summary_outpath):
         os.makedirs(summary_outpath)
     smfile = GetConvexHullDataSummaryFileName(encMethod, codecName, preset,
@@ -449,10 +446,10 @@
         # location in summary excel file
         row = 1
         rdcolstart = CvxHDataStartCol + 1
-        for (cls, contents) in ContentsDict.items():
+        for (cls, clip_list) in ClipDict.items():
             sht.write(row, 0, cls)
-            for content in contents:
-                key = GetShortContentName(content)
+            for clip in clip_list:
+                key = GetShortContentName(clip.file_name)
                 sht.write(row, 1, key)
                 for resfile in resultfiles:
                     if key in resfile:
diff --git a/tools/convexhull_framework/src/ScalingTest.py b/tools/convexhull_framework/src/ScalingTest.py
index aa26d80..510b075 100644
--- a/tools/convexhull_framework/src/ScalingTest.py
+++ b/tools/convexhull_framework/src/ScalingTest.py
@@ -15,11 +15,14 @@
 import xlrd
 import logging
 from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
-from VideoScaler import GetDownScaledOutFile, DownScaling, UpScaling, GetUpScaledOutFile
-from Config import DnScaleRatio, FrameNum, Clips, QualityList, LoggerName, Path_ScalingResults, ScalQty_WtCols, \
-    ScalQty_startRow, LineColors, ScalSumQty_WtCols, ContentPath
-from Utils import GetVideoInfo, Cleanfolder, GetShortContentName, CreateChart_Scatter, AddSeriesToChart_Scatter, UpdateChart, \
-    InsertChartsToSheet, CalcRowsClassAndContentDict, GetContents, CreateChart_Line, AddSeriesToChart_Line
+from VideoScaler import GetDownScaledOutFile, DownScaling, UpScaling,\
+     GetUpScaledOutFile
+from Config import DnScaleRatio, FrameNum, QualityList, LoggerName,\
+     Path_ScalingResults, ScalQty_WtCols, ScalQty_startRow, LineColors, \
+     ScalSumQty_WtCols
+from Utils import Cleanfolder, GetShortContentName, CreateChart_Scatter, \
+     AddSeriesToChart_Scatter, UpdateChart, InsertChartsToSheet, \
+     CalcRowsClassAndContentDict, Clip
 
 subloggername = "ScalingTest"
 loggername = LoggerName + '.' + '%s' % subloggername
@@ -34,22 +37,17 @@
     return file
 
 def GetScalingResultExcelFile_PerContent(content):
-    filename = GetShortContentName(content)
+    filename = GetShortContentName(content, False)
     filename = "ScalingResults_%s.xlsx" % filename
     file = os.path.join(Path_ScalingResults, filename)
     return file
 
-def Run_Scaling_Test(content, dnScalAlgo, upScalAlgo, path_dnscl, path_upscl,
-                     path_log, path_cfg, savememory, keepupscaledyuv):
+def Run_Scaling_Test(clip, dnScalAlgo, upScalAlgo, path_dnscl, path_upscl,
+                     path_log, path_cfg, savememory, keepupscaledyuv,
+                     LogCmdOnly=False):
     logger.info("start running scaling tests with content %s"
-                % os.path.basename(content))
-    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
-    if totalnum < FrameNum:
-        logger.error("content %s includes total %d frames < specified % frames!"
-                     % (content, totalnum, FrameNum))
-        return
-
-    DnScaledRes = [(int(width / ratio), int(height / ratio))
+                % os.path.basename(clip.file_name))
+    DnScaledRes = [(int(clip.width / ratio), int(clip.height / ratio))
                    for ratio in DnScaleRatio]
     for i in range(len(DnScaledRes)):
         if savememory:
@@ -59,39 +57,38 @@
 
         DnScaledW = DnScaledRes[i][0]
         DnScaledH = DnScaledRes[i][1]
-        logger.info("start downscaling content to %dx%d"
-                    % (DnScaledW, DnScaledH))
+        logger.info("start downscaling content to %dx%d" % (DnScaledW, DnScaledH))
         # downscaling
-        dnscalyuv = GetDownScaledOutFile(content, width, height, DnScaledW,
-                                         DnScaledH, path_dnscl, dnScalAlgo)
+        dnscalyuv = GetDownScaledOutFile(clip, DnScaledW, DnScaledH,
+                                         path_dnscl, dnScalAlgo)
         if not os.path.isfile(dnscalyuv):
-            dnscalyuv = DownScaling(content, FrameNum, width, height, DnScaledW,
-                                    DnScaledH, path_dnscl, dnScalAlgo)
-
-        upscaleyuv = UpScaling(dnscalyuv, FrameNum, DnScaledW, DnScaledH, width,
-                               height, path_upscl, upScalAlgo)
-
-        CalculateQualityMetric(content, FrameNum, upscaleyuv, width, height,
-                               path_log, path_cfg)
-
+            dnscalyuv = DownScaling(clip, FrameNum, DnScaledW, DnScaledH,
+                                    path_dnscl, path_cfg, dnScalAlgo, LogCmdOnly)
+        dnscaled_clip = Clip(GetShortContentName(dnscalyuv, False)+'.y4m',
+                             dnscalyuv, "", DnScaledW, DnScaledH,
+                             clip.fmt, clip.fps_num, clip.fps_denom,
+                             clip.bit_depth)
+        upscaleyuv = UpScaling(dnscaled_clip, FrameNum, clip.width, clip.height,
+                               path_upscl, path_cfg, upScalAlgo, LogCmdOnly)
+        CalculateQualityMetric(clip.file_path, FrameNum, upscaleyuv, clip.fmt,
+                               clip.width, clip.height, clip.bit_depth,
+                               path_log, LogCmdOnly)
     if savememory:
         Cleanfolder(path_dnscl)
 
     logger.info("finish running scaling test.")
 
-def GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content, scaleRatios,
-                                path_log):
-    contshortname = GetShortContentName(content)
-    logger.info("start generate excel file for content :%s" % contshortname)
-    excFile = GetScalingResultExcelFile_PerContent(content)
+def GeneratePerClipExcelFile(dnScalAlgos, upScalAlgos, clip, path_log):
+    logger.info("start generate excel file for content :%s" % clip.file_name)
+    excFile = GetScalingResultExcelFile_PerContent(clip.file_name)
     wb = xlsxwriter.Workbook(excFile)
-    shtname = contshortname
+    shtname = GetShortContentName(clip.file_name)
     sht = wb.add_worksheet(shtname)
 
     sht.write(1, 0, 'Content Name')
-    sht.write(2, 0, contshortname)
+    sht.write(2, 0, clip.file_name)
     sht.write(1, 1, 'Scaling Ratio')
-    sht.write_column(2, 1, scaleRatios)
+    sht.write_column(2, 1, DnScaleRatio)
     pre_title = ['Width', 'Height', 'DnScaledWidth', 'DnScaledHeight']
     sht.write_row(1, 2, pre_title)
     for dn_algo, up_algo, col in zip(dnScalAlgos, upScalAlgos, ScalQty_WtCols):
@@ -99,13 +96,12 @@
         sht.write(0, col, algos)
         sht.write_row(1, col, QualityList)
 
-    cls, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
-    rows = [ScalQty_startRow + i for i in range(len(scaleRatios))]
+    rows = [ScalQty_startRow + i for i in range(len(DnScaleRatio))]
     continfos = []
-    for ratio, row in zip(scaleRatios, rows):
-        dw = int(w / ratio)
-        dh = int(h / ratio)
-        info = [w, h, dw, dh]
+    for ratio, row in zip(DnScaleRatio, rows):
+        dw = int(clip.width / ratio)
+        dh = int(clip.height / ratio)
+        info = [clip.width, clip.height, dw, dh]
         sht.write_row(row, 2, info)
         continfos.append(info)
 
@@ -121,15 +117,17 @@
                                         range(len(dnScalAlgos))):
         qualities = []
         seriname = dn_algo + '--' + up_algo
-        for ratio, row, idx in zip(scaleRatios, rows, range(len(scaleRatios))):
+        for ratio, row, idx in zip(DnScaleRatio, rows, range(len(DnScaleRatio))):
             w = continfos[idx][0]
             h = continfos[idx][1]
             dw = continfos[idx][2]
             dh = continfos[idx][3]
-            dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh,
-                                             path_log, dn_algo)
-            upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h,
-                                           path_log, up_algo)
+            dnScalOut = GetDownScaledOutFile(clip, dw, dh, path_log, dn_algo)
+            ds_clip = Clip(GetShortContentName(dnScalOut, False) + '.y4m',
+                 dnScalOut, "", dw, dh, clip.fmt, clip.fps_num, clip.fps_denom,
+                 clip.bit_depth)
+            upScalOut = GetUpScaledOutFile(ds_clip, clip.width, clip.height,
+                                           up_algo, path_log)
             qtys = GatherQualityMetrics(upScalOut, path_log)
             sht.write_row(row, col, qtys)
             qualities.append(qtys)
@@ -175,16 +173,15 @@
         sht.write_row(1, col, QualityList)
 
     content_infos = {}; totalnum_contents = 0
-    for (clss, contents), row_clss in zip(ContentsDict.items(), Rows_Class):
+    for (clss, clip_list), row_clss in zip(ClipDict.items(), Rows_Class):
         sht.write(row_clss, 0, clss)
-        totalnum_contents = totalnum_contents + len(contents)
-        for content, row_cont in zip(contents, range(len(contents))):
-            cl, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
-            dw = int(w / ratio)
-            dh = int(h / ratio)
-            shortcntname = GetShortContentName(content)
+        totalnum_contents = totalnum_contents + len(clip_list)
+        for clip, row_cont in zip(clip_list, range(len(clip_list))):
+            dw = int(clip.width / ratio)
+            dh = int(clip.height / ratio)
+            shortcntname = GetShortContentName(clip.file_name, False)
             sht.write(row_clss + row_cont, 2, shortcntname)
-            infos = [w, h, dw, dh]
+            infos = [clip.width, clip.height, dw, dh]
             sht.write_row(row_clss + row_cont, 3, infos)
             content_infos[shortcntname] = infos
 
@@ -202,17 +199,18 @@
                                         range(len(dnScalAlgos))):
         qualities = []
         seriname = dn_algo + '--' + up_algo
-        for (clss, contents), row_clss in zip(ContentsDict.items(), Rows_Class):
-            for content, row_cont in zip(contents, range(len(contents))):
-                key = GetShortContentName(content)
+        for (clss, clip_list), row_clss in zip(ClipDict.items(), Rows_Class):
+            for clip, row_cont in zip(clip_list, range(len(clip_list))):
+                key = GetShortContentName(clip.file_name, False)
                 w = content_infos[key][0]
                 h = content_infos[key][1]
                 dw = content_infos[key][2]
                 dh = content_infos[key][3]
-                dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh,
-                                                 path_log, dn_algo)
-                upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h,
-                                               path_log, up_algo)
+                dnScalOut = GetDownScaledOutFile(clip, dw, dh, path_log, dn_algo)
+                ds_clip = Clip(GetShortContentName(dnScalOut, False) + '.y4m',
+                               dnScalOut, clss, dw, dh, clip.fmt, clip.fps_num,
+                               clip.fps_denom, clip.bit_depth)
+                upScalOut = GetUpScaledOutFile(ds_clip, w, h, up_algo, path_log)
                 qtys = GatherQualityMetrics(upScalOut, path_log)
                 sht.write_row(row_clss + row_cont, col, qtys)
                 qualities.append(qtys)
@@ -260,7 +258,7 @@
         sht.write(0, col, algos)
         sht.write_row(1, col, StatsMetrics)
 
-    step = len(ContentsDict) + 1  # 1 extra row for total of each class
+    step = len(ClipDict) + 1  # 1 extra row for total of each class
     startrow = 2
     rows_qtymtr = [startrow + step * i for i in range(len(QualityList))]
     for qty, row_qm, y in zip(QualityList, rows_qtymtr, range(len(QualityList))):
@@ -272,11 +270,11 @@
         #charts.append(chart)
 
         totalnum_contents = 0
-        for (cls, contents), idx, rdrow_cls in zip(ContentsDict.items(),
-                                                   range(len(ContentsDict)),
+        for (cls, clip_list), idx, rdrow_cls in zip(ClipDict.items(),
+                                                   range(len(ClipDict)),
                                                    Rows_Class):
             sht.write(row_qm + idx, 1, cls)
-            num_content = len(contents)
+            num_content = len(clip_list)
             totalnum_contents = totalnum_contents + num_content
             sht.write(row_qm + idx, 2, num_content)
             for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg):
@@ -302,7 +300,7 @@
                 sht.write(row_qm + idx, wtcol + 3, formula)
 
         #write total contents statistics
-        wtrow = row_qm + len(ContentsDict)
+        wtrow = row_qm + len(ClipDict)
         sht.write(wtrow, 1, 'Total')
         sht.write(wtrow, 2, totalnum_contents)
         for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg):
@@ -330,28 +328,25 @@
 
     logger.info("finish average sheet for ratio:%2.2f." % ratio)
 
-
-def SaveScalingResultsToExcel(dnScalAlgos, upScalAlgos, path_log):
+def SaveScalingResultsToExcel(dnScalAlgos, upScalAlgos, clip_list, path_log):
     logger.info("start saving scaling quality results to excel files.......")
     if not os.path.exists(Path_ScalingResults):
         os.makedirs(Path_ScalingResults)
 
     logger.info("start generating per content scaling quality excel file.......")
-    contents = GetContents(ContentPath, Clips)
-    for content in contents:
-        GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content,
-                                    DnScaleRatio, path_log)
+    for clip in clip_list:
+        GeneratePerClipExcelFile(dnScalAlgos, upScalAlgos, clip, path_log)
 
     scaleRatios = DnScaleRatio
-    scaleRatios.remove(1.0)
+    if (1.0 in scaleRatios):
+        scaleRatios.remove(1.0)
     logger.info("start generating scaling quality summary excel file.......")
-    sumexcFile = GetScalingResultExcelFile(len(scaleRatios), len(DnScaleRatio))
+    sumexcFile = GetScalingResultExcelFile(len(scaleRatios), len(dnScalAlgos))
     wb = xlsxwriter.Workbook(sumexcFile)
     # to generate rows number of starting of each class: Rows_Class
-    global ContentsDict, Rows_Class
-    ContentsDict, Rows_Class = CalcRowsClassAndContentDict(ScalQty_startRow,
-                                                           ContentPath, Clips)
-
+    global ClipDict, Rows_Class
+    ClipDict, Rows_Class = CalcRowsClassAndContentDict(ScalQty_startRow,
+                                                       clip_list)
     sumShts = []
     for ratio in scaleRatios:
         sht = GenerateSummarySheet(wb, dnScalAlgos, upScalAlgos, ratio, path_log)
diff --git a/tools/convexhull_framework/src/Utils.py b/tools/convexhull_framework/src/Utils.py
index ccac884..7023239 100644
--- a/tools/convexhull_framework/src/Utils.py
+++ b/tools/convexhull_framework/src/Utils.py
@@ -12,10 +12,36 @@
 
 import os
 import re
+import sys
 import subprocess
 import time
 import logging
-from Config import LogLevels, Have_Class_Subfolder
+from Config import LogLevels, ContentPath
+from AV2CTCVideo import Y4M_CLIPs, CTC_TEST_SET, AS_TEST_SET
+
+class Clip:
+    file_name = ""
+    file_path = ""
+    file_class = ""
+    width = 0
+    height = 0
+    fmt = ""
+    fps_num = 0
+    fps_denom = 0
+    fps = 0
+    bit_depth = 0
+
+    def __init__(self, Name="", Path = "", Class="", Width=0, Height=0, Fmt="", FPS_num=0, FPS_denom=0, Bit_depth=0):
+        self.file_name = Name
+        self.file_path = Path
+        self.file_class = Class
+        self.width = Width
+        self.height = Height
+        self.fmt = Fmt
+        self.fps_num = FPS_num
+        self.fps_denom = FPS_denom
+        self.fps = round(self.fps_num / self.fps_denom)
+        self.bit_depth = Bit_depth
 
 def Cleanfolder(folder):
     if os.path.isdir(folder):
@@ -44,73 +70,64 @@
         name = basename
     return name
 
-def GetContents(contentpath, clips):
-    contents = []
-    for key, val in clips.items():
-        folder = contentpath
-        if Have_Class_Subfolder:
-            cls = val[0]
-            folder = os.path.join(contentpath, cls)
+def parseY4MHeader(y4m):
+    """
+    Parse y4m information from its header.
+    """
+    w = 0; h = 0; fps_num = 0; fps_denom = 0; fr = 0; fmt = "420"; bit_depth = 8;
+    #print("parsing " + y4m)
+    with open(y4m, 'rb') as f:
+        line = f.readline().decode('utf-8')
+        #YUV4MPEG2 W4096 H2160 F30000:1001 Ip A0:0 C420p10 XYSCSS=420P10
+        m = re.search(r"W([0-9]+) H([0-9]+) F([0-9]+)\:([0-9]+)", line)
+        if m:
+            w = int(m.group(1))
+            h = int(m.group(2))
+            fps_num = float(m.group(3))
+            fps_denom = float(m.group(4))
+            fps = round(fps_num / fps_denom)
+        m = re.search(r"C([0-9]+)p([0-9]+)", line)
+        if m:
+            fmt = m.group(1)
+            bit_depth = int(m.group(2))
+    if w == 0 or h == 0 or fps == 0:
+        print("Failed to parse the input y4m file!\n")
+        sys.exit()
+    return (w, h, fps_num, fps_denom, fps, fmt, bit_depth)
 
-        file = os.path.join(folder, key) + ".yuv"
-        if os.path.isfile(file):
-            contents.append(file)
+def CreateClipList(test_cfg):
+    clip_list = []; test_set = []
+    #[filename, class, width, height, fps_num, fps_denom, bitdepth, fmt]
+    test_set = AS_TEST_SET if (test_cfg == 'AS') else CTC_TEST_SET
 
-    return contents
+    for cls in test_set:
+        for file in Y4M_CLIPs[cls]:
+            y4m = os.path.join(ContentPath, cls, file)
+            w, h, fps_num, fps_denom, fps, fmt, bit_depth = parseY4MHeader(y4m)
+            clip = Clip(file, y4m, cls, w, h, fmt, fps_num, fps_denom, bit_depth)
+            clip_list.append(clip)
+    return clip_list
 
-def GetVideoInfo(content, Clips):
-    basename = GetShortContentName(content, False)
-    cls = Clips[basename][0]
-    width = Clips[basename][1]
-    height = Clips[basename][2]
-    fr = Clips[basename][3]
-    bitdepth = Clips[basename][4]
-    fmt = Clips[basename][5]
-
-    #default for 8 bit 420
-    RatioForFrameSize = 3/2
-    if fmt == 'yuv422p':
-        RatioForFrameSize = 2
-    elif fmt == 'yuv444p':
-        RatioForFrameSize = 3
-    if bitdepth > 8:
-        RatioForFrameSize *= 2
-
-    totalnum = os.path.getsize(content) / (width * height * RatioForFrameSize)
-
-    return cls, width, height, fr, bitdepth, fmt, totalnum
-
-def GetContentDict(contentpath, clips):
+def GetContentDict(clip_list):
     dict = {}
-
-    if Have_Class_Subfolder:
-        for key, val in clips.items():
-            cls = val[0]
-            folder = os.path.join(contentpath, cls)
-            file = os.path.join(folder, key) + ".yuv"
-            if os.path.isfile(file):
-                if cls in dict:
-                    if file not in dict[cls]:
-                        dict[cls].append(file)
-                else:
-                    dict[cls] = [file]
-    else:
-        # this is for case no subfolder/class. As * is forbidden for folder name,
-        # no folder will be same as this
-        cls = "*All"
-        dict[cls] = GetContents(contentpath, clips)
-
+    for clip in clip_list:
+        cls = clip.file_class
+        file = clip.file_path
+        if os.path.isfile(file):
+            if cls in dict:
+                if clip not in dict[cls]:
+                    dict[cls].append(clip)
+            else:
+                dict[cls] = [clip]
     return dict
 
-def CalcRowsClassAndContentDict(rowstart, content_path, clips, times=1):
-    contentsdict = GetContentDict(content_path, clips)
-
+def CalcRowsClassAndContentDict(rowstart, clip_list, times=1):
+    contentsdict = GetContentDict(clip_list)
     ofstc = rowstart
     rows_class = []
-    for cls, contents in contentsdict.items():
+    for cls, clips in contentsdict.items():
         rows_class.append(ofstc)
-        ofstc = ofstc + len(contents) * times
-
+        ofstc = ofstc + len(clips) * times
     return contentsdict, rows_class
 
 
@@ -223,13 +240,13 @@
 
     if logcmdonly or level != 0:
         global CmdLogger
-        logfilename = os.path.join(path, 'ConvexHullTestCmd_%s.log'
-                                   % time.strftime("%Y%m%d-%H%M%S"))
+        logfilename = os.path.join(path, '%s_TestCmd_%s.log'
+                                   % (name, time.strftime("%Y%m%d-%H%M%S")))
         CmdLogger = open(logfilename, 'w')
 
     if level != 0:
-        logfilename = os.path.join(path, 'ConvexHullTest_%s.log'
-                                   % time.strftime("%Y%m%d-%H%M%S"))
+        logfilename = os.path.join(path, '%s_Test_%s.log'
+                                   % (name, time.strftime("%Y%m%d-%H%M%S")))
         hdlr = logging.FileHandler(logfilename)
         formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
         hdlr.setFormatter(formatter)
diff --git a/tools/convexhull_framework/src/VideoDecoder.py b/tools/convexhull_framework/src/VideoDecoder.py
index 84146bd..8841d3b 100644
--- a/tools/convexhull_framework/src/VideoDecoder.py
+++ b/tools/convexhull_framework/src/VideoDecoder.py
@@ -11,23 +11,17 @@
 __author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
 
 import Utils
-from Config import BinPath, LogCmdOnly, FFMPEG, AOMDEC
+from Config import AOMDEC
 from Utils import ExecuteCmd
 
-#use ffmpeg to decode bitstream
-def DecodeWithFfmpeg(infile, outfile):
-    args = " -y -i %s -pix_fmt yuv420p -c:v rawvideo %s" % (infile, outfile)
-    cmd = FFMPEG + args
-    ExecuteCmd(cmd, LogCmdOnly)
-
-def DecodeWithAOM(infile, outfile):
-    args = " --codec=av1 --i420 --rawvideo --summary -o %s %s" % (outfile, infile)
+def DecodeWithAOM(infile, outfile, LogCmdOnly=False):
+    args = " --codec=av1 --summary -o %s %s" % (outfile, infile)
     cmd = AOMDEC + args
     ExecuteCmd(cmd, LogCmdOnly)
 
-def VideoDecode(codec, infile, outfile):
+def VideoDecode(codec, infile, outfile, LogCmdOnly=False):
     Utils.CmdLogger.write("::Decode\n")
-    if codec == 'AV1':
-        DecodeWithAOM(infile, outfile)
+    if codec == 'av1':
+        DecodeWithAOM(infile, outfile, LogCmdOnly)
     else:
-        DecodeWithFfmpeg(infile, outfile)
+        raise ValueError("invalid parameter for decode.")
diff --git a/tools/convexhull_framework/src/VideoEncoder.py b/tools/convexhull_framework/src/VideoEncoder.py
index 412e710..89e7c43 100644
--- a/tools/convexhull_framework/src/VideoEncoder.py
+++ b/tools/convexhull_framework/src/VideoEncoder.py
@@ -11,72 +11,53 @@
 __author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
 
 import Utils
-from Config import BinPath, LogCmdOnly, FFMPEG, AOMENC, SVTAV1, SVTHEVC
+from Config import AOMENC, SVTAV1
 from Utils import ExecuteCmd
 
-#use ffmpeg to encode video
-def EncodeWithFfmpeg_HEVC(infile, QP, num,fr, width, height, outfile, preset):
-    EncodeProfile = 'main'
-    args = " -y -s %dx%d -pix_fmt yuv420p  -r %s -i %s -frames:v %d -g %d -bf 7" \
-           " -bsf:v hevc_mp4toannexb -c:v libx265 -preset %s -profile:v %s " \
-           "-x265-params \"qp=%d:aq-mode=0:b-adapt=0:bframes=7:b-pyramid=1:" \
-           "no-scenecut=1:no-open-gop=1:input-depth=8:output-depth=8\" %s" % (
-            width, height, fr, infile, num, int(2 * fr),  # gop size = 2 seconds
-            preset, EncodeProfile, (QP+3), outfile)
-    cmd = FFMPEG + args
-    ExecuteCmd(cmd, LogCmdOnly)
+def EncodeWithAOM_AV1(clip, test_cfg, QP, framenum, outfile, preset,
+                      LogCmdOnly=False):
+    args = " --verbose --codec=av1 -v --psnr --obu --frame-parallel=0" \
+           " --cpu-used=%s --limit=%d --auto-alt-ref=1 --passes=1" \
+           " --end-usage=q --i%s --threads=1  --end-usage=q" \
+           " --use-fixed-qp-offsets=1 --deltaq-mode=0 --enable-tpl-model=0" \
+           " --enable-keyframe-filtering=0 --fps=%d/%d --input-bit-depth=%d" \
+           " --bit-depth=%d --qp=%d -w %d -h %d" \
+           % (preset, framenum, clip.fmt, clip.fps_num, clip.fps_denom, clip.bit_depth,
+              clip.bit_depth, 4*QP, clip.width, clip.height)
 
-def EncodeWithAOM_AV1(infile, QP, framenum, framerate, width, height, outfile,
-                      preset):
-    args = " --verbose --codec=av1 -v --psnr --ivf  --frame-parallel=0 --cpu-used=%s" \
-           " --limit=%d --auto-alt-ref=1 --passes=1 --end-usage=q --i420" \
-           " --min-gf-interval=16 --max-gf-interval=16 --gf-min-pyr-height=4 " \
-           " --gf-max-pyr-height=4 --threads=1 --lag-in-frames=19 --end-usage=q " \
-           " --kf-min-dist=65 --kf-max-dist=65 --use-fixed-qp-offsets=1 --deltaq-mode=0 " \
-           " --enable-tpl-model=0  --enable-keyframe-filtering=0 " \
-           " --fps=60/1 --input-bit-depth=8 --qp=%d -w %d -h %d -o %s %s"\
-           % (preset, framenum, 4*QP, width, height, outfile, infile)
+    if test_cfg == "RA" or test_cfg == "AS":
+        args += " --min-gf-interval=16 --max-gf-interval=16 --gf-min-pyr-height=4" \
+                " --gf-max-pyr-height=4 --kf-min-dist=65 --kf-max-dist=65" \
+                " --lag-in-frames=19"
+    elif test_cfg == "LD":
+        args += " --kf-min-dist=9999 --kf-max-dist=9999 --lag-in-frames=0" \
+                " --subgop-config-str=ld"
+    else:
+        print("Unsupported Test Configuration %s" % test_cfg)
+    args += " -o %s %s" % (outfile, clip.file_path)
     cmd = AOMENC + args
     ExecuteCmd(cmd, LogCmdOnly)
 
-def EncodeWithSVT_AV1(infile, QP, framenum, framerate, width, height, outfile,
-                      preset):
+def EncodeWithSVT_AV1(clip, test_cfg, QP, framenum, outfile, preset,
+                      LogCmdOnly=False):
+    #TODO: update svt parameters
     args = " --preset %s --scm 2 --lookahead 0 --hierarchical-levels 3 -n %d" \
            " --keyint 255 -rc 0 -q %d -w %d -h %d -b %s -i %s"\
-           % (str(preset), framenum, QP, width, height, outfile, infile)
+           % (str(preset), framenum, QP, clip.width, clip.height, outfile,
+              clip.file_path)
     cmd = SVTAV1 + args
     ExecuteCmd(cmd, LogCmdOnly)
 
-def EncodeWithSVT_HEVC(infile, QP, framenum, framerate, width, height, outfile,
-                       preset):
-    args = " -i %s  -w %d -h %d -encMode %s -hierarchical-levels 3" \
-           " -intra-period 100 -scd 0 -rc 0 -q %d -n %d -b %s"\
-           % (infile, width, height, preset, QP, framenum, outfile)
-    cmd = SVTHEVC + args
-    ExecuteCmd(cmd, LogCmdOnly)
-
-def VideoEncode(EncodeMethod, CodecName, infile, QP, framenum, framerate, width,
-                height, outfile, preset):
+def VideoEncode(EncodeMethod, CodecName, clip, test_cfg, QP, framenum, outfile,
+                preset, LogCmdOnly=False):
     Utils.CmdLogger.write("::Encode\n")
-    if EncodeMethod == "ffmpeg":
-        if CodecName == 'hevc':
-            EncodeWithFfmpeg_HEVC(infile, QP, framenum, framerate, width, height,
-                                  outfile, preset)
-        else:
-            raise ValueError("invalid parameter for encode.")
-    elif EncodeMethod == "aom":
-        if CodecName == 'av1':
-            EncodeWithAOM_AV1(infile, QP, framenum, framerate, width, height,
-                              outfile, preset)
-        else:
-            raise ValueError("invalid parameter for encode.")
-    elif EncodeMethod == "svt":
-        if CodecName == 'av1':
-            EncodeWithSVT_AV1(infile, QP, framenum, framerate, width, height,
-                              outfile, preset)
-        elif CodecName == 'hevc':
-            EncodeWithSVT_HEVC(infile, QP, framenum, framerate, width, height,
-                               outfile, preset)
+    if CodecName == 'av1':
+        if EncodeMethod == "aom":
+            EncodeWithAOM_AV1(clip, test_cfg, QP, framenum, outfile, preset,
+                              LogCmdOnly)
+        elif EncodeMethod == "svt":
+            EncodeWithSVT_AV1(clip, test_cfg, QP, framenum, outfile, preset,
+                              LogCmdOnly)
         else:
             raise ValueError("invalid parameter for encode.")
     else:
diff --git a/tools/convexhull_framework/src/VideoScaler.py b/tools/convexhull_framework/src/VideoScaler.py
index 51e85c0..25af1b3 100644
--- a/tools/convexhull_framework/src/VideoScaler.py
+++ b/tools/convexhull_framework/src/VideoScaler.py
@@ -13,77 +13,124 @@
 import os
 import Utils
 import logging
-from Config import BinPath, LogCmdOnly, LoggerName, FFMPEG
+import fileinput
+from shutil import copyfile
+from Config import LoggerName, FFMPEG, HDRToolsConfigFileTemplate, HDRConvert
 from Utils import GetShortContentName, ExecuteCmd
 
 subloggername = "VideoScaler"
 loggername = LoggerName + '.' + '%s' % subloggername
 logger = logging.getLogger(loggername)
 
-def ValidAlgo_ffmpeg(algo):
-    if (algo == 'bicubic' or algo == 'lanczos' or algo == 'sinc' or
-        algo == 'bilinear' or algo == 'spline' or algo == 'gauss' or
-        algo == 'bicublin' or algo == 'neighbor'):
-        return True
-    else:
-        return False
+def GenerateCfgFile(clip, outw, outh, algo, outfile, num, configpath):
+    contentBaseName = GetShortContentName(clip.file_name, False)
+    cfg_filename = contentBaseName + ('_Scaled_%s_%dx%d.cfg'% (algo, outw, outh))
+    fmt = 1
+    if (clip.fmt == '400'):
+        fmt = 0
+    elif (clip.fmt == '420'):
+        fmt = 1
+    elif (clip.fmt == '422'):
+        fmt = 2
+    elif (clip.fmt == '444'):
+        fmt = 3
 
-#use ffmpeg to do image rescaling for yuv420 8bit
-def RescaleWithFfmpeg(infile, inw, inh, outw, outh, algo, outfile, num, app_path):
-    args = " -y -s:v %dx%d -i %s -vf scale=%dx%d -c:v rawvideo -pix_fmt yuv420p" \
-           " -sws_flags %s+accurate_rnd+full_chroma_int+full_chroma_inp+bitexact"\
-           "+print_info -sws_dither none" \
-           % (inw, inh, infile, outw, outh, algo)
-    if (algo == 'lanczos'):
-        args += " -param0 5 "
-    args += " -frames %d %s" % (num, outfile)
-    cmd = FFMPEG + args
+    cfgfile = os.path.join(configpath, cfg_filename)
+    copyfile(HDRToolsConfigFileTemplate, cfgfile)
+    fp = fileinput.input(cfgfile, inplace=1)
+    for line in fp:
+        if 'SourceFile=' in line:
+            line = 'SourceFile="%s"\n' % clip.file_path
+        if 'OutputFile=' in line:
+            line = 'OutputFile="%s"\n' % outfile
+        if 'SourceWidth=' in line:
+            line = 'SourceWidth=%d\n' % clip.width
+        if 'SourceHeight=' in line:
+            line = 'SourceHeight=%d\n' % clip.height
+        if 'OutputWidth=' in line:
+            line = 'OutputWidth=%d\n' % outw
+        if 'OutputHeight=' in line:
+            line = 'OutputHeight=%d\n' % outh
+        if 'ScalingMode=' in line:
+            line = 'ScalingMode=3\n'
+        if 'LanczosLobes=' in line:
+            line = 'LanczosLobes=5\n'
+        if 'SourceRate=' in line:
+            line = 'SourceRate=%4.4f\n' % (float)(clip.fps_num / clip.fps_denom)
+        if 'SourceChromaFormat=' in line:
+            line = 'SourceChromaFormat=%d\n' % fmt
+        if 'SourceBitDepthCmp0=' in line:
+            line = 'SourceBitDepthCmp0=%d\n' % clip.bit_depth
+        if 'SourceBitDepthCmp1=' in line:
+            line = 'SourceBitDepthCmp1=%d\n' % clip.bit_depth
+        if 'SourceBitDepthCmp2=' in line:
+            line = 'SourceBitDepthCmp2=%d\n' % clip.bit_depth
+        if 'OutputRate=' in line:
+            line = 'OutputRate=%4.4f\n' % (float)(clip.fps_num / clip.fps_denom)
+        if 'OutputChromaFormat=' in line:
+            line = 'OutputChromaFormat=%d\n' % fmt
+        if 'OutputBitDepthCmp0=' in line:
+            line = 'OutputBitDepthCmp0=%d\n' % clip.bit_depth
+        if 'OutputBitDepthCmp1=' in line:
+            line = 'OutputBitDepthCmp1=%d\n' % clip.bit_depth
+        if 'OutputBitDepthCmp2=' in line:
+            line = 'OutputBitDepthCmp2=%d\n' % clip.bit_depth
+        if 'NumberOfFrames=' in line:
+            line = 'NumberOfFrames=%d\n' % num
+        print(line, end='')
+    fp.close()
+    return cfgfile
+
+def RescaleWithHDRTool(clip, outw, outh, algo, outfile, num, cfg_path,
+                       LogCmdOnly = False):
+    cfg_file = GenerateCfgFile(clip, outw, outh, algo, outfile, num, cfg_path)
+    args = " -f %s" % cfg_file
+    cmd = HDRConvert + args
     ExecuteCmd(cmd, LogCmdOnly)
 
-def VideoRescaling(infile, num, inw, inh, outw, outh, outfile, algo):
-    if ValidAlgo_ffmpeg(algo):
-        RescaleWithFfmpeg(infile, inw, inh, outw, outh, algo, outfile, num, BinPath)
+def VideoRescaling(clip, num, outw, outh, outfile, algo, cfg_path,
+                   LogCmdOnly = False):
+    RescaleWithHDRTool(clip, outw, outh, algo, outfile, num, cfg_path, LogCmdOnly)
     # add other tools for scaling here later
-    else:
-        logger.error("unsupported scaling algorithm.")
 
 ####################################################################################
 ##################### Major Functions ################################################
-def GetDownScaledOutFile(input, inw, inh, dnw, dnh, path, algo):
-    contentBaseName = GetShortContentName(input)
-    actual_algo = 'None' if inw == dnw and inh == dnh else algo
-    filename = contentBaseName + ('_DnScaled_%s_%dx%d.yuv' % (actual_algo, dnw,
+def GetDownScaledOutFile(clip, dnw, dnh, path, algo):
+    contentBaseName = GetShortContentName(clip.file_name, False)
+    actual_algo = 'None' if clip.width == dnw and clip.height == dnh else algo
+    filename = contentBaseName + ('_Scaled_%s_%dx%d.y4m' % (actual_algo, dnw,
                                                               dnh))
     dnscaledout = os.path.join(path, filename)
     return dnscaledout
 
-def GetUpScaledOutFile(infile, inw, inh, outw, outh, path, algo):
-    actual_algo = 'None' if inw == outw and inh == outh else algo
-    filename = GetShortContentName(infile, False) + ('_UpScaled_%s_%dx%d.yuv'
-                                                     % (actual_algo, outw, outh))
+def GetUpScaledOutFile(clip, outw, outh, algo, path):
+    actual_algo = 'None' if clip.width == outw and clip.height == outh else algo
+    filename = GetShortContentName(clip.file_name, False) + \
+               ('_Scaled_%s_%dx%d.y4m' % (actual_algo, outw, outh))
     upscaledout = os.path.join(path, filename)
     return upscaledout
 
-def DownScaling(input, num, inw, inh, outw, outh, path, algo):
-    dnScalOut = GetDownScaledOutFile(input, inw, inh, outw, outh, path, algo)
+def DownScaling(clip, num, outw, outh, path, cfg_path, algo, LogCmdOnly = False):
+    dnScalOut = GetDownScaledOutFile(clip, outw, outh, path, algo)
 
     Utils.CmdLogger.write("::Downscaling\n")
-    if (inw == outw and inh == outh):
-        cmd = "copy %s %s" % (input, dnScalOut)
+    if (clip.width == outw and clip.height == outh):
+        cmd = "copy %s %s" % (clip.file_path, dnScalOut)
         ExecuteCmd(cmd, LogCmdOnly)
     else:
         # call separate process to do the downscaling
-        VideoRescaling(input, num, inw, inh, outw, outh, dnScalOut, algo)
+        VideoRescaling(clip, num, outw, outh, dnScalOut, algo, cfg_path,
+                       LogCmdOnly)
     return dnScalOut
 
-def UpScaling(infile, num, inw, inh, outw, outh, path, algo):
-    upScaleOut = GetUpScaledOutFile(infile, inw, inh, outw, outh, path, algo)
-
+def UpScaling(clip, num, outw, outh, path, cfg_path, algo, LogCmdOnly = False):
+    upScaleOut = GetUpScaledOutFile(clip, outw, outh, algo, path)
     Utils.CmdLogger.write("::Upscaling\n")
-    if (inw == outw and inh == outh):
-        cmd = "copy %s %s" % (infile, upScaleOut)
+    if (clip.width == outw and clip.height == outh):
+        cmd = "copy %s %s" % (clip.file_path, upScaleOut)
         ExecuteCmd(cmd, LogCmdOnly)
     else:
         # call separate process to do the upscaling
-        VideoRescaling(infile, num, inw, inh, outw, outh, upScaleOut, algo)
+        VideoRescaling(clip, num, outw, outh, upScaleOut, algo, cfg_path,
+                       LogCmdOnly)
     return upScaleOut