/*!
 * \copy
 *     Copyright (c)  2013, Cisco Systems
 *     All rights reserved.
 *
 *     Redistribution and use in source and binary forms, with or without
 *     modification, are permitted provided that the following conditions
 *     are met:
 *
 *        * Redistributions of source code must retain the above copyright
 *          notice, this list of conditions and the following disclaimer.
 *
 *        * Redistributions in binary form must reproduce the above copyright
 *          notice, this list of conditions and the following disclaimer in
 *          the documentation and/or other materials provided with the
 *          distribution.
 *
 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *     COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *     POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#include <stdarg.h>

#ifdef ONLY_ENC_FRAMES_NUM
#undef ONLY_ENC_FRAMES_NUM
#endif//ONLY_ENC_FRAMES_NUM
#define ONLY_ENC_FRAMES_NUM		INT_MAX // 2, INT_MAX	// type the num you try to encode here, 2, 10, etc



//#define STICK_STREAM_SIZE

#include "measure_time.h"
#include "param_svc.h"
//#include "layered_pic_buffer.h"
#include "read_config.h"

#include "typedefs.h"

#ifdef _MSC_VER
#include <io.h>     /* _setmode() */
#include <fcntl.h>  /* _O_BINARY */
#endif//_MSC_VER

#include "codec_def.h"
#include "codec_api.h"
#include "extern.h"
#include "macros.h"
#include "wels_const.h"
#include "logging.h"

#ifdef MT_ENABLED
#include "mt_defs.h"
#include "WelsThreadLib.h"
#endif//MT_ENABLED

#ifdef WIN32
#ifdef WINAPI_FAMILY
#include <winapifamily.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define HAVE_PROCESS_AFFINITY
#endif
#else /* defined(WINAPI_FAMILY) */
#define HAVE_PROCESS_AFFINITY
#endif
#endif /* WIN32 */

#include <iostream>
using namespace std;
using namespace WelsSVCEnc;

/*
 *	Layer Context
 */
typedef struct LayerpEncCtx_s {
  int32_t				iDLayerQp;
  SMulSliceOption	sMso;
} SLayerPEncCtx;



/* Ctrl-C handler */
static int     g_iCtrlC = 0;
static void    SigIntHandler (int a) {
  g_iCtrlC = 1;
}

int ParseConfig (CReadConfig& cRdCfg, SWelsSvcCodingParam& pSvcParam, SFilesSet& sFileSet) {
  string strTag[4];
  int32_t iLeftTargetBitrate = 0;
  int32_t	iLeftSpatialBitrate[MAX_DEPENDENCY_LAYER] = { 0 };
  int32_t iRet = 0;
  int8_t iLayerCount = 0;
  string str_ ("SlicesAssign");
  const int kiSize = str_.size();

//	memset(&pSvcParam, 0, sizeof(WelsSVCParamConfig));

  while (!cRdCfg.EndOfFile()) {
    long iRd = cRdCfg.ReadLine (&strTag[0]);
    if (iRd > 0) {
      if (strTag[0].empty())
        continue;
      if (strTag[0].compare ("OutputFile") == 0) {
        sFileSet.strBsFile	= strTag[1];
      } else if (strTag[0].compare ("MaxFrameRate") == 0) {
        pSvcParam.fMaxFrameRate	= (float)atof (strTag[1].c_str());
      } else if (strTag[0].compare ("FramesToBeEncoded") == 0) {
        pSvcParam.uiFrameToBeCoded	= atoi (strTag[1].c_str());
      } else if (strTag[0].compare ("SourceSequenceInRGB24") == 0) {
        pSvcParam.iInputCsp	= atoi (strTag[1].c_str()) == 0 ? videoFormatI420 : videoFormatRGB;
      } else if (strTag[0].compare ("GOPSize") == 0) {
        pSvcParam.uiGopSize	= atoi (strTag[1].c_str());
      } else if (strTag[0].compare ("IntraPeriod") == 0) {
        pSvcParam.uiIntraPeriod	= atoi (strTag[1].c_str());
      } else if (strTag[0].compare ("EnableSpsPpsIDAddition") == 0) {
        pSvcParam.bEnableSpsPpsIdAddition	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableScalableSEI") == 0) {
        pSvcParam.bEnableSSEI	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableFrameCropping") == 0) {
        pSvcParam.bEnableFrameCroppingFlag = (atoi (strTag[1].c_str()) != 0);
      } else if (strTag[0].compare ("LoopFilterDisableIDC") == 0) {
        pSvcParam.iLoopFilterDisableIdc	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iLoopFilterDisableIdc > 6 || pSvcParam.iLoopFilterDisableIdc < 0) {
          fprintf (stderr, "Invalid parameter in iLoopFilterDisableIdc: %d.\n", pSvcParam.iLoopFilterDisableIdc);
          iRet = 1;
          break;
        }
      } else if (strTag[0].compare ("LoopFilterAlphaC0Offset") == 0) {
        pSvcParam.iLoopFilterAlphaC0Offset	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iLoopFilterAlphaC0Offset < -6)
          pSvcParam.iLoopFilterAlphaC0Offset	= -6;
        else if (pSvcParam.iLoopFilterAlphaC0Offset > 6)
          pSvcParam.iLoopFilterAlphaC0Offset	= 6;
      } else if (strTag[0].compare ("LoopFilterBetaOffset") == 0) {
        pSvcParam.iLoopFilterBetaOffset	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iLoopFilterBetaOffset < -6)
          pSvcParam.iLoopFilterBetaOffset	= -6;
        else if (pSvcParam.iLoopFilterBetaOffset > 6)
          pSvcParam.iLoopFilterBetaOffset	= 6;
      } else if (strTag[0].compare ("InterLayerLoopFilterDisableIDC") == 0) {
        pSvcParam.iInterLayerLoopFilterDisableIdc = (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iInterLayerLoopFilterDisableIdc > 6 || pSvcParam.iInterLayerLoopFilterDisableIdc < 0) {
          fprintf (stderr, "Invalid parameter in iInterLayerLoopFilterDisableIdc: %d.\n",
                   pSvcParam.iInterLayerLoopFilterDisableIdc);
          iRet = 1;
          break;
        }
      } else if (strTag[0].compare ("InterLayerLoopFilterAlphaC0Offset") == 0) {
        pSvcParam.iInterLayerLoopFilterAlphaC0Offset	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iInterLayerLoopFilterAlphaC0Offset < -6)
          pSvcParam.iInterLayerLoopFilterAlphaC0Offset	= -6;
        else if (pSvcParam.iInterLayerLoopFilterAlphaC0Offset > 6)
          pSvcParam.iInterLayerLoopFilterAlphaC0Offset	= 6;
      } else if (strTag[0].compare ("InterLayerLoopFilterBetaOffset") == 0) {
        pSvcParam.iInterLayerLoopFilterBetaOffset	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iInterLayerLoopFilterBetaOffset < -6)
          pSvcParam.iInterLayerLoopFilterBetaOffset	= -6;
        else if (pSvcParam.iInterLayerLoopFilterBetaOffset > 6)
          pSvcParam.iInterLayerLoopFilterBetaOffset	= 6;
      } else if (strTag[0].compare ("MultipleThreadIdc") == 0) {
        // # 0: auto(dynamic imp. internal encoder); 1: multiple threads imp. disabled; > 1: count number of threads;
        pSvcParam.iMultipleThreadIdc	= atoi (strTag[1].c_str());
        if (pSvcParam.iMultipleThreadIdc < 0)
          pSvcParam.iMultipleThreadIdc = 0;
        else if (pSvcParam.iMultipleThreadIdc > MAX_THREADS_NUM)
          pSvcParam.iMultipleThreadIdc = MAX_THREADS_NUM;
      } else if (strTag[0].compare ("EnableRC") == 0) {
        pSvcParam.bEnableRc	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("RCMode") == 0) {
        pSvcParam.iRCMode	= atoi (strTag[1].c_str());
      } else if (strTag[0].compare ("TargetBitrate") == 0) {
        pSvcParam.iTargetBitrate	= 1000 * atoi (strTag[1].c_str());
        if (pSvcParam.bEnableRc && pSvcParam.iTargetBitrate <= 0) {
          fprintf (stderr, "Invalid target bitrate setting due to RC enabled. Check TargetBitrate field please!\n");
          return 1;
        }
        if (pSvcParam.bEnableRc) {
          iLeftTargetBitrate	= pSvcParam.iTargetBitrate;
        }
      } else if (strTag[0].compare ("EnableDenoise") == 0) {
        pSvcParam.bEnableDenoise	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableSceneChangeDetection") == 0) {
        pSvcParam.bEnableSceneChangeDetect	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableBackgroundDetection") == 0) {
        pSvcParam.bEnableBackgroundDetection	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableAdaptiveQuantization") == 0) {
        pSvcParam.bEnableAdaptiveQuant	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableFrameSkip") == 0) {
        pSvcParam.bEnableFrameSkip	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("EnableLongTermReference") == 0) {
        pSvcParam.bEnableLongTermReference	= atoi (strTag[1].c_str()) ? true : false;
      } else if (strTag[0].compare ("LtrMarkPeriod") == 0) {
        pSvcParam.uiLtrMarkPeriod	= (uint32_t)atoi (strTag[1].c_str());
      } else if (strTag[0].compare ("NumLayers") == 0) {
        pSvcParam.iNumDependencyLayer	= (int8_t)atoi (strTag[1].c_str());
        if (pSvcParam.iNumDependencyLayer > MAX_DEPENDENCY_LAYER || pSvcParam.iNumDependencyLayer <= 0) {
          fprintf (stderr, "Invalid parameter in iNumDependencyLayer: %d.\n", pSvcParam.iNumDependencyLayer);
          iRet = 1;
          break;
        }
      } else if (strTag[0].compare ("LayerCfg") == 0) {
        if (strTag[1].length() > 0)
          sFileSet.sSpatialLayers[iLayerCount].strLayerCfgFile	= strTag[1];
//				pSvcParam.sDependencyLayers[iLayerCount].uiDependencyId	= iLayerCount;
        ++ iLayerCount;
      } else if (strTag[0].compare ("PrefixNALAddingCtrl") == 0) {
        int ctrl_flag = atoi (strTag[1].c_str());
        if (ctrl_flag > 1)
          ctrl_flag	= 1;
        else if (ctrl_flag < 0)
          ctrl_flag	= 0;
        pSvcParam.bPrefixNalAddingCtrl	= ctrl_flag ? true : false;
      }
    }
  }

  const int8_t kiActualLayerNum = WELS_MIN (pSvcParam.iNumDependencyLayer, iLayerCount);
  if (pSvcParam.iNumDependencyLayer >
      kiActualLayerNum) {	// fixed number of dependency layer due to parameter error in settings
    pSvcParam.iNumDependencyLayer	= kiActualLayerNum;
  }

  assert (kiActualLayerNum <= MAX_DEPENDENCY_LAYER);

  for (int8_t iLayer = 0; iLayer < kiActualLayerNum; ++ iLayer) {
    SLayerPEncCtx sLayerCtx;
    int32_t iLayerArg = -2;
    int32_t iNumQualityBitrateLayerSet = 0;

    SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
    CReadConfig cRdLayerCfg (sFileSet.sSpatialLayers[iLayer].strLayerCfgFile);

    memset (&sLayerCtx, 0, sizeof (SLayerPEncCtx));

    if (!cRdLayerCfg.ExistFile()) {
      fprintf (stderr, "Unabled to open layer #%d configuration file: %s.\n", iLayer, cRdLayerCfg.GetFileName().c_str());
      continue;
    }

    while (!cRdLayerCfg.EndOfFile()) {
      long iLayerRd = cRdLayerCfg.ReadLine (&strTag[0]);
      bool_t bFound = false;
      if (iLayerRd > 0) {
        if (strTag[0].empty())
          continue;
        if (strTag[0].compare ("SourceWidth") == 0) {
          pDLayer->iFrameWidth	= atoi (strTag[1].c_str());
          pDLayer->iActualWidth = pDLayer->iFrameWidth;
        } else if (strTag[0].compare ("SourceHeight") == 0) {
          pDLayer->iFrameHeight	= atoi (strTag[1].c_str());
          pDLayer->iActualHeight	= pDLayer->iFrameHeight;
        } else if (strTag[0].compare ("FrameRateIn") == 0) {
          pDLayer->fInputFrameRate	= (float)atof (strTag[1].c_str());
        } else if (strTag[0].compare ("FrameRateOut") == 0) {
          pDLayer->fOutputFrameRate = (float)atof (strTag[1].c_str());
        } else if (strTag[0].compare ("InputFile") == 0) {
          if (strTag[1].length() > 0)
            sFileSet.sSpatialLayers[iLayer].strSeqFile	= strTag[1];
        } else if (strTag[0].compare ("ReconFile") == 0) {
          const int kiLen = strTag[1].length();
          if (kiLen >= MAX_FNAME_LEN)
            return 1;
#ifdef ENABLE_FRAME_DUMP
          pDLayer->sRecFileName[kiLen] = '\0';
          strncpy (pDLayer->sRecFileName, strTag[1].c_str(), kiLen);	// confirmed_safe_unsafe_usage
#endif//ENABLE_FRAME_DUMP
        } else if (strTag[0].compare ("ProfileIdc") == 0) {
          pDLayer->uiProfileIdc	= atoi (strTag[1].c_str());
        } else if (strTag[0].compare ("FRExt") == 0) {
//					pDLayer->frext_mode	= (bool_t)atoi(strTag[1].c_str());
        } else if (strTag[0].compare ("SpatialBitrate") == 0) {
          pDLayer->iSpatialBitrate	= 1000 * atoi (strTag[1].c_str());
          if (pSvcParam.bEnableRc && pDLayer->iSpatialBitrate <= 0) {
            fprintf (stderr, "Invalid spatial bitrate(%d) in dependency layer #%d.\n", pDLayer->iSpatialBitrate, iLayer);
            return 1;
          }
          if (pSvcParam.bEnableRc && pDLayer->iSpatialBitrate > iLeftTargetBitrate) {
            fprintf (stderr, "Invalid spatial(#%d) bitrate(%d) setting due to unavailable left(%d)!\n", iLayer,
                     pDLayer->iSpatialBitrate, iLeftTargetBitrate);
            return 1;
          }
          iLeftSpatialBitrate[iLayer]	= pDLayer->iSpatialBitrate;
        } else if (strTag[0].compare ("InitialQP") == 0) {
          sLayerCtx.iDLayerQp	= atoi (strTag[1].c_str());
        } else if (strTag[0].compare ("SliceMode") == 0) {
          sLayerCtx.sMso.uiSliceMode	= (SliceMode)atoi (strTag[1].c_str());
        } else if (strTag[0].compare ("SliceSize") == 0) { //SM_DYN_SLICE
          sLayerCtx.sMso.sSliceArgument.uiSliceSizeConstraint	= (SliceMode)atoi (strTag[1].c_str());
          continue;
        } else if (strTag[0].compare ("SliceNum") == 0) {
          sLayerCtx.sMso.sSliceArgument.iSliceNum = atoi (strTag[1].c_str());
        } else if (strTag[0].compare (0, kiSize, str_) == 0) {
          const char* kpString = strTag[0].c_str();
          int uiSliceIdx = atoi (&kpString[kiSize]);
          assert (uiSliceIdx < MAX_SLICES_NUM);
          sLayerCtx.sMso.sSliceArgument.uiSliceMbNum[uiSliceIdx] = atoi (strTag[1].c_str());
        }
      }
    }
    pDLayer->iDLayerQp	= sLayerCtx.iDLayerQp;
    pDLayer->sMso.uiSliceMode		= sLayerCtx.sMso.uiSliceMode;

    memcpy (&pDLayer->sMso, &sLayerCtx.sMso, sizeof (SMulSliceOption));	// confirmed_safe_unsafe_usage
    memcpy (&pDLayer->sMso.sSliceArgument.uiSliceMbNum[0], &sLayerCtx.sMso.sSliceArgument.uiSliceMbNum[0],
            sizeof (sLayerCtx.sMso.sSliceArgument.uiSliceMbNum));	// confirmed_safe_unsafe_usage
  }

  return iRet;
}

int ParseCommandLine (int argc, char** argv, SVCEncodingParam& sParam) {
  char* pCmd;
  int i = 0;

  while (i < argc) {
    pCmd = argv[i++];

    if (!strcmp (pCmd, "-numl") && (i < argc))
      sParam.iSpatialLayerNum = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-numt") && (i < argc))
      sParam.iTemporalLayerNum = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-iper") && (i < argc))
      sParam.iIntraPeriod = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-spsid") && (i < argc))
      sParam.bEnableSpsPpsIdAddition = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-denois") && (i < argc))
      sParam.bEnableDenoise = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-bgd") && (i < argc))
      sParam.bEnableBackgroundDetection = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-aq") && (i < argc))
      sParam.bEnableAdaptiveQuant = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-fs") && (i < argc))
      sParam.bEnableFrameSkip = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-ltr") && (i < argc))
      sParam.bEnableLongTermReference = atoi (argv[i++]) ? true : false;

    else if (!strcmp (pCmd, "-ltrper") && (i < argc))
      sParam.iLtrMarkPeriod = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-rcm") && (i < argc))
      sParam.iRCMode = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-tarb") && (i < argc))
      sParam.iTargetBitrate = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-ltarb") && (i + 1 < argc)) {
      int iLayer = atoi (argv[i++]);
      sParam.sSpatialLayers[iLayer].iSpatialBitrate = atoi (argv[i++]);
    }

    else if (!strcmp (pCmd, "-trace") && (i < argc))
      WelsStderrSetTraceLevel (atoi (argv[i++]));

    else if (!strcmp (pCmd, "-sw") && (i < argc))
      sParam.iPicWidth = atoi (argv[i++]);

    else if (!strcmp (pCmd, "-sh") && (i < argc))
      sParam.iPicHeight = atoi (argv[i++]);
  }

  return 0;
}

void PrintHelp() {
  printf ("\n Wels SVC Encoder Usage:\n\n");
  printf (" Syntax: welsenc.exe -h\n");
  printf (" Syntax: welsenc.exe welsenc.cfg\n");
  printf (" Syntax: welsenc.exe welsenc.cfg [options]\n");

  printf ("\n Supported Options:\n");
  printf ("  -bf     Bit Stream File\n");
  printf ("  -frms   Number of total frames to be encoded\n");
  printf ("  -gop    GOPSize - GOP size (2,4,8,16,32,64, default: 1)\n");
  printf ("  -iper   Intra period (default: -1) : must be a power of 2 of GOP size (or -1)\n");
  printf ("  -spsid   Enable id adding in SPS/PPS per IDR \n");
  printf ("  -denois Control denoising  (default: 0)\n");
  printf ("  -scene  Control scene change detection (default: 0)\n");
  printf ("  -bgd    Control background detection (default: 0)\n");
  printf ("  -aq     Control adaptive quantization (default: 0)\n");
  printf ("  -ltr    Control long term reference (default: 0)\n");
  printf ("  -rc	  Control rate control: 0-disable; 1-enable \n");
  printf ("  -tarb	  Overall target bitrate\n");
  printf ("  -numl   Number Of Layers: Must exist with layer_cfg file and the number of input layer_cfg file must equal to the value set by this command\n");
  printf ("  The options below are layer-based: (need to be set with layer id)\n");
  printf ("  -org		(Layer) (original file); example: -org 0 src.yuv\n");
  printf ("  -drec		(Layer) (reconstruction file); Setting the reconstruction file, this will only functioning when dumping reconstruction is enabled\n");
  printf ("  -sw		(Layer) (source width)\n");
  printf ("  -sh		(Layer) (source height)\n");
  printf ("  -frin		(Layer) (input frame rate)\n");
  printf ("  -frout  	(Layer) (output frame rate)\n");
  printf ("  -lqp		(Layer) (base quality layer qp : must work with -ldeltaqp or -lqparr)\n");
  printf ("  -ltarb	    (Layer) (spatial layer target bitrate)\n");
  printf ("  -slcmd   (Layer) (spatial layer slice mode): pls refer to layerX.cfg for details ( -slcnum: set target slice num; -slcsize: set target slice size constraint ) \n");
  printf ("\n");
}

int ParseCommandLine (int argc, char** argv, SWelsSvcCodingParam& pSvcParam, SFilesSet& sFileSet) {
  char* pCommand = NULL;
  SLayerPEncCtx sLayerCtx[3];
  int n = 0;
  string str_ ("SlicesAssign");
  const int kiSize = str_.size();

  while (n < argc) {
    pCommand = argv[n++];

    if (!strcmp (pCommand, "-bf") && (n < argc))
      sFileSet.strBsFile.assign (argv[n++]);

    else if (!strcmp (pCommand, "-frms") && (n < argc))
      pSvcParam.uiFrameToBeCoded = atoi (argv[n++]);

    else if (!strcmp (pCommand, "-gop") && (n < argc))
      pSvcParam.uiGopSize = atoi (argv[n++]);

    else if (!strcmp (pCommand, "-iper") && (n < argc))
      pSvcParam.uiIntraPeriod = atoi (argv[n++]);

    else if (!strcmp (pCommand, "-spsid") && (n < argc))
      pSvcParam.bEnableSpsPpsIdAddition = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-denois") && (n < argc))
      pSvcParam.bEnableDenoise = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-scene") && (n < argc))
      pSvcParam.bEnableSceneChangeDetect = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-bgd") && (n < argc))
      pSvcParam.bEnableBackgroundDetection = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-aq") && (n < argc))
      pSvcParam.bEnableAdaptiveQuant = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-fs") && (n < argc))
      pSvcParam.bEnableFrameSkip = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-ltr") && (n < argc))
      pSvcParam.bEnableLongTermReference = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-ltrper") && (n < argc))
      pSvcParam.uiLtrMarkPeriod = atoi (argv[n++]);

    else if (!strcmp (pCommand, "-rc") && (n < argc))
      pSvcParam.bEnableRc = atoi (argv[n++]) ? true : false;

    else if (!strcmp (pCommand, "-tarb") && (n < argc))
      pSvcParam.iTargetBitrate = atoi (argv[n++]);

    else if (!strcmp (pCommand, "-numl") && (n < argc)) {
      pSvcParam.iNumDependencyLayer = atoi (argv[n++]);
      for (int ln = 0 ; (ln < pSvcParam.iNumDependencyLayer) && (n < argc) ; ln++) {
//				pSvcParam.sDependencyLayers[ln].uiDependencyId = ln;
        sFileSet.sSpatialLayers[ln].strLayerCfgFile.assign (argv[n++]);
      }

      for (int8_t iLayer = 0; iLayer < pSvcParam.iNumDependencyLayer; ++ iLayer) {
        SLayerPEncCtx sLayerCtx;
        string strTag[4];
        int32_t iLayerArg = -2;
        int32_t iNumQualityBitrateLayerSet = 0;

        SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
        CReadConfig cRdLayerCfg (sFileSet.sSpatialLayers[iLayer].strLayerCfgFile);

        memset (&sLayerCtx, 0, sizeof (SLayerPEncCtx));

//				pDLayer->frext_mode = 0;
        if (!cRdLayerCfg.ExistFile()) {
          fprintf (stderr, "Unabled to open layer #%d configuration file: %s.\n", iLayer, cRdLayerCfg.GetFileName().c_str());
          continue;
        }

        while (!cRdLayerCfg.EndOfFile()) {
          long iLayerRd = cRdLayerCfg.ReadLine (&strTag[0]);
          if (iLayerRd > 0) {
            if (strTag[0].empty())
              continue;
            if (strTag[0].compare ("SourceWidth") == 0) {
              pDLayer->iFrameWidth	= atoi (strTag[1].c_str());
              pDLayer->iActualWidth = pDLayer->iFrameWidth;
            } else if (strTag[0].compare ("SourceHeight") == 0) {
              pDLayer->iFrameHeight	= atoi (strTag[1].c_str());
              pDLayer->iActualHeight	= pDLayer->iFrameHeight;
            } else if (strTag[0].compare ("FrameRateIn") == 0) {
              pDLayer->fInputFrameRate	= (float)atof (strTag[1].c_str());
            } else if (strTag[0].compare ("FrameRateOut") == 0) {
              pDLayer->fOutputFrameRate = (float)atof (strTag[1].c_str());
            } else if (strTag[0].compare ("InputFile") == 0) {
              if (strTag[1].length() > 0)
                sFileSet.sSpatialLayers[iLayer].strSeqFile = strTag[1];
            } else if (strTag[0].compare ("ReconFile") == 0) {
#ifdef ENABLE_FRAME_DUMP
              const int kiLen = strTag[1].length();
              if (kiLen >= MAX_FNAME_LEN)
                return 1;
              pDLayer->sRecFileName[kiLen] = '\0';
              strncpy (pDLayer->sRecFileName, strTag[1].c_str(), kiLen);	// confirmed_safe_unsafe_usage
#endif//ENABLE_FRAME_DUMP
            } else if (strTag[0].compare ("ProfileIdc") == 0) {
              pDLayer->uiProfileIdc	= atoi (strTag[1].c_str());
            } else if (strTag[0].compare ("FRExt") == 0) {
//							pDLayer->frext_mode	= (bool_t)atoi(strTag[1].c_str());
            } else if (strTag[0].compare ("SpatialBitrate") == 0) {
              pDLayer->iSpatialBitrate	= 1000 * atoi (strTag[1].c_str());
            } else if (strTag[0].compare ("InitialQP") == 0) {
              sLayerCtx.iDLayerQp	= atoi (strTag[1].c_str());
            } else if (strTag[0].compare ("SliceMode") == 0) {
              sLayerCtx.sMso.uiSliceMode	= (SliceMode)atoi (strTag[1].c_str());
            } else if (strTag[0].compare ("SliceSize") == 0) { //SM_DYN_SLICE
              sLayerCtx.sMso.sSliceArgument.uiSliceSizeConstraint	= (SliceMode)atoi (strTag[1].c_str());
            } else if (strTag[0].compare ("SliceNum") == 0) {
              sLayerCtx.sMso.sSliceArgument.iSliceNum = atoi (strTag[1].c_str());
            } else if (strTag[0].compare (0, kiSize, str_) == 0) {
              const char* kpString = strTag[0].c_str();
              int uiSliceIdx = atoi (&kpString[kiSize]);
              assert (uiSliceIdx < MAX_SLICES_NUM);
              sLayerCtx.sMso.sSliceArgument.uiSliceMbNum[uiSliceIdx] = atoi (strTag[1].c_str());
            }
          }
        }
        pDLayer->iDLayerQp		= sLayerCtx.iDLayerQp;
        pDLayer->sMso.uiSliceMode		= sLayerCtx.sMso.uiSliceMode;
        memcpy (&pDLayer->sMso, &sLayerCtx.sMso, sizeof (SMulSliceOption));	// confirmed_safe_unsafe_usage
        memcpy (&pDLayer->sMso.sSliceArgument.uiSliceMbNum[0], &sLayerCtx.sMso.sSliceArgument.uiSliceMbNum[0],
                sizeof (sLayerCtx.sMso.sSliceArgument.uiSliceMbNum));	// confirmed_safe_unsafe_usage

      }
    }

    else if (!strcmp (pCommand, "-org") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      sFileSet.sSpatialLayers[iLayer].strSeqFile.assign (argv[n++]);
    }

    else if (!strcmp (pCommand, "-drec") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      const int iLen = strlen (argv[n]);
#ifdef ENABLE_FRAME_DUMP
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->sRecFileName[iLen] = '\0';
      strncpy (pDLayer->sRecFileName, argv[n++], iLen);	// confirmed_safe_unsafe_usage
#endif//ENABLE_FRAME_DUMP
    }

    else if (!strcmp (pCommand, "-sw") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->iFrameWidth =  atoi (argv[n++]);
      pDLayer->iActualWidth = pDLayer->iFrameWidth;
    }

    else if (!strcmp (pCommand, "-sh") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->iFrameHeight =  atoi (argv[n++]);
      pDLayer->iActualHeight = pDLayer->iFrameHeight;
    }

    else if (!strcmp (pCommand, "-frin") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->fInputFrameRate = (float)atof (argv[n++]);
    }

    else if (!strcmp (pCommand, "-frout") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->fOutputFrameRate = (float)atof (argv[n++]);
    }

    else if (!strcmp (pCommand, "-lqp") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->iDLayerQp = sLayerCtx[iLayer].iDLayerQp =  atoi (argv[n++]);
    }
    //sLayerCtx[iLayer].num_quality_layers = pDLayer->num_quality_layers = 1;

    else if (!strcmp (pCommand, "-ltarb") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->iSpatialBitrate	= 1000 * atoi (argv[n++]);
    }

    else if (!strcmp (pCommand, "-slcmd") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];

      switch (atoi (argv[n++])) {
      case 0:
        pDLayer->sMso.uiSliceMode = SM_SINGLE_SLICE;
        break;
      case 1:
        pDLayer->sMso.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
        break;
      case 2:
        pDLayer->sMso.uiSliceMode = SM_RASTER_SLICE;
        break;
      case 3:
        pDLayer->sMso.uiSliceMode = SM_ROWMB_SLICE;
        break;
      case 4:
        pDLayer->sMso.uiSliceMode = SM_DYN_SLICE;
        break;
      default:
        pDLayer->sMso.uiSliceMode = SM_RESERVED;
        break;
      }
    }

    else if (!strcmp (pCommand, "-slcsize") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->sMso.sSliceArgument.uiSliceSizeConstraint = atoi (argv[n++]);
    }

    else if (!strcmp (pCommand, "-slcnum") && (n + 1 < argc)) {
      unsigned int	iLayer = atoi (argv[n++]);
      SDLayerParam* pDLayer = &pSvcParam.sDependencyLayers[iLayer];
      pDLayer->sMso.sSliceArgument.iSliceNum = atoi (argv[n++]);
    }
  }
  return 0;
}



int FillSpecificParameters (SVCEncodingParam& sParam) {
  /* Test for temporal, spatial, SNR scalability */
  sParam.fFrameRate	= 30.0f;		// input frame rate
  sParam.iPicWidth		= 1280;			// width of picture in samples
  sParam.iPicHeight	= 720;			// height of picture in samples
  sParam.iTargetBitrate = 2500000;		// target bitrate desired
  sParam.iRCMode       = 0;            //  rc mode control
  sParam.iTemporalLayerNum = 3;	// layer number at temporal level
  sParam.iSpatialLayerNum	= 4;	// layer number at spatial level
  sParam.bEnableDenoise    = 0;    // denoise control
  sParam.bEnableBackgroundDetection = 1; // background detection control
  sParam.bEnableAdaptiveQuant       = 1; // adaptive quantization control
  sParam.bEnableFrameSkip           = 1; // frame skipping
  sParam.bEnableLongTermReference  = 0; // long term reference control
  sParam.iLtrMarkPeriod = 30;

  sParam.iInputCsp			= videoFormatI420;			// color space of input sequence
  sParam.iKeyPicCodingMode = 1; // mode of key picture coding
  sParam.iIntraPeriod		= 320;		// period of Intra frame
  sParam.bEnableSpsPpsIdAddition = 1;
  sParam.bPrefixNalAddingCtrl = 1;

  int iIndexLayer = 0;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth	= 160;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight	= 90;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate	= 7.5f;
  sParam.sSpatialLayers[iIndexLayer].iQualityLayerNum	    = 1;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate		= 64000;
  sParam.sSpatialLayers[iIndexLayer].iCgsSnrRefined		= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[0]	= 0;
//	memset(sParam.iTemporalBitrate, 0, sizeof(sParam.iTemporalBitrate));
  sParam.sSpatialLayers[iIndexLayer].iInterSpatialLayerPredFlag	= 0;
#ifdef MT_ENABLED
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = 0;
#endif

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth	= 320;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight	= 180;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate	= 15.0f;
  sParam.sSpatialLayers[iIndexLayer].iQualityLayerNum	    = 1;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate		= 160000;
  sParam.sSpatialLayers[iIndexLayer].iCgsSnrRefined		= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[0]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[1]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[2]	= 0;
  sParam.sSpatialLayers[iIndexLayer].iInterSpatialLayerPredFlag	= 0;
#ifdef MT_ENABLED
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = 0;
#endif

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth	= 640;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight	= 360;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate	= 30.0f;
  sParam.sSpatialLayers[iIndexLayer].iQualityLayerNum	    = 1;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate		= 512000;
  sParam.sSpatialLayers[iIndexLayer].iCgsSnrRefined		= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[0]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[1]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[2]	= 0;
  sParam.sSpatialLayers[iIndexLayer].iInterSpatialLayerPredFlag	= 0;
#ifdef MT_ENABLED
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = 0;
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.sSliceArgument.uiSliceNum = 1;
#endif

  ++ iIndexLayer;
  sParam.sSpatialLayers[iIndexLayer].iVideoWidth	= 1280;
  sParam.sSpatialLayers[iIndexLayer].iVideoHeight	= 720;
  sParam.sSpatialLayers[iIndexLayer].fFrameRate	= 30.0f;
  sParam.sSpatialLayers[iIndexLayer].iQualityLayerNum	    = 1;
  sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate		= 1500000;
  sParam.sSpatialLayers[iIndexLayer].iCgsSnrRefined		= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[0]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[1]	= 0;
//	sParam.sSpatialLayers[iIndexLayer].iQualityBitrate[2]	= 0;
  sParam.sSpatialLayers[iIndexLayer].iInterSpatialLayerPredFlag	= 0;
#ifdef MT_ENABLED
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.uiSliceMode = 0;
  sParam.sSpatialLayers[iIndexLayer].sSliceCfg.sSliceArgument.uiSliceNum = 1;
#endif

  float fMaxFr = sParam.sSpatialLayers[sParam.iSpatialLayerNum - 1].fFrameRate;
  for (int32_t i = sParam.iSpatialLayerNum - 2; i >= 0; -- i) {
    if (sParam.sSpatialLayers[i].fFrameRate > fMaxFr + EPSN)
      fMaxFr = sParam.sSpatialLayers[i].fFrameRate;
  }
  sParam.fFrameRate = fMaxFr;

  return 0;
}

/* For SVC Demo test */
int ProcessEncodingSvcWithParam (ISVCEncoder* pPtrEnc, int argc, char** argv) {
  const char* kpSrcFile = argv[1];
  const char* kpStrBsFile = argv[2];

  if (pPtrEnc == NULL || kpSrcFile == NULL || kpStrBsFile == NULL)
    return 1;

  FILE* pFpBs = NULL;
  FILE* pFpSrc = NULL;
  SFrameBSInfo sFbi;
  SVCEncodingParam sSvcParam;
  int64_t iStart = 0, iTotal = 0;
  int32_t ret = 0;

  int32_t iPicLumaSize = 0;
  int32_t iFrameSize = 0;
  uint8_t* pPlanes[3] = { 0 };
  int32_t iFrame = 0;

#if defined ( STICK_STREAM_SIZE )
  FILE* fTrackStream = fopen ("coding_size.stream", "wb");;
#endif

  pFpSrc	= fopen (kpSrcFile, "rb");
  if (NULL == pFpSrc)
    return 1;
  pFpBs	= fopen (kpStrBsFile, "wb");
  if (NULL == pFpBs) {
    fclose (pFpSrc);
    pFpSrc = NULL;
    return 1;
  }

  memset (&sFbi, 0, sizeof (SFrameBSInfo));
  memset (&sSvcParam, 0, sizeof (SVCEncodingParam));

  FillSpecificParameters (sSvcParam);

  int iParsedNum = 3;
  if (ParseCommandLine (argc - iParsedNum, argv + iParsedNum, sSvcParam) != 0) {
    printf ("parse pCommand line failed\n");
    ret = 1;
    goto ERROR_RET;
  }

  if (cmResultSuccess != pPtrEnc->Initialize (&sSvcParam, INIT_TYPE_PARAMETER_BASED)) {
    fprintf (stderr, "Encoder Initialization failed!\n");
	ret = 1;
    goto ERROR_RET;
   }

  iPicLumaSize = sSvcParam.iPicWidth * sSvcParam.iPicHeight;
  switch (sSvcParam.iInputCsp) {
    int iStride;
  case videoFormatI420:
  case videoFormatYV12:
    iFrameSize  = (3 * iPicLumaSize) >> 1;
    pPlanes[0]	= new uint8_t[iFrameSize];
    pPlanes[1]	= pPlanes[0] + iPicLumaSize;
    pPlanes[2]	= pPlanes[1]	+ (iPicLumaSize >> 2);
    break;
  case videoFormatYUY2:
  case videoFormatYVYU:
  case videoFormatUYVY:
    iStride      = CALC_BI_STRIDE (sSvcParam.iPicWidth,  16);
    iFrameSize  = iStride * sSvcParam.iPicHeight;
    pPlanes[0]   = new uint8_t[iFrameSize];
    break;
  case videoFormatRGB:
  case videoFormatBGR:
    iStride      = CALC_BI_STRIDE (sSvcParam.iPicWidth,  24);
    iFrameSize  = iStride * sSvcParam.iPicHeight;
    pPlanes[0]	= new uint8_t[iFrameSize];
    break;
  case videoFormatBGRA:
  case videoFormatRGBA:
  case videoFormatARGB:
  case videoFormatABGR:
    iStride = 4 * sSvcParam.iPicWidth;
    iFrameSize  = iStride * sSvcParam.iPicHeight;
    pPlanes[0]	= new uint8_t[iFrameSize];
    break;
  default:
    ret = 1;
    goto ERROR_RET;
  }

  while (true) {
    if (feof (pFpSrc))
      break;
#ifdef ONLY_ENC_FRAMES_NUM
    if (iFrame >= ONLY_ENC_FRAMES_NUM)
      break;
#endif//ONLY_ENC_FRAMES_NUM
    if (fread (pPlanes[0], sizeof (uint8_t), iFrameSize, pFpSrc) <= 0)
      break;

    iStart	= WelsTime();
    long iEncode = pPtrEnc->EncodeFrame (pPlanes[0], &sFbi);
    iTotal += WelsTime() - iStart;
    if (videoFrameTypeInvalid == iEncode) {
      fprintf (stderr, "EncodeFrame() failed: %ld.\n", iEncode);
      break;
    }

    /* Write bit-stream */
    if (pFpBs != NULL && videoFrameTypeSkip != iEncode) {	// file handler to write bit stream
      int iLayer = 0;
      while (iLayer < sFbi.iLayerNum) {
        SLayerBSInfo* pLayerBsInfo = &sFbi.sLayerInfo[iLayer];
        if (pLayerBsInfo != NULL) {
          int iLayerSize = 0;
          int iNalIdx = pLayerBsInfo->iNalCount - 1;
          do {
            iLayerSize += pLayerBsInfo->iNalLengthInByte[iNalIdx];
            -- iNalIdx;
          } while (iNalIdx >= 0);
          fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs);	// write pure bit stream into file
        }
        ++ iLayer;
      }
      ++ iFrame;
    }
  }

  if (iFrame > 0) {
    double dElapsed = iTotal / 1e6;
    printf ("Frames:		%d\nencode time:	%f sec\nFPS:		%f fps\n", iFrame, dElapsed, (iFrame * 1.0) / dElapsed);
  }

  if (NULL != pPlanes[0]) {
    delete [] pPlanes[0];
    pPlanes[0] = NULL;
  }
ERROR_RET:
  if (pFpBs) {
    fclose (pFpBs);
    pFpBs = NULL;
  }
  if (pFpSrc) {
    fclose (pFpSrc);
    pFpSrc = NULL;
  }

  return ret;
}


int ProcessEncodingSvcWithConfig (ISVCEncoder* pPtrEnc, int argc, char** argv) {
  int iRet				= 0;

  if (pPtrEnc == NULL)
    return 1;

  SFrameBSInfo sFbi;
  SWelsSvcCodingParam sSvcParam;
  int64_t iStart = 0, iTotal = 0;

  // Preparing encoding process
  FILE* pFileYUV[MAX_DEPENDENCY_LAYER] = {0};
  int32_t iActualFrameEncodedCount = 0;
  int32_t iFrameIdx = 0;
  int32_t	iTotalFrameMax = -1;
  int8_t  iDlayerIdx = 0;
  uint8_t* pYUV[MAX_DEPENDENCY_LAYER] = { 0 };
  SSourcePicture**    pSrcPicList = NULL;
#if (defined(RUN_SIMULATOR) || defined(_WIN32)||defined(_MACH_PLATFORM) || (defined(__GNUC__)))
  // Inactive with sink with output file handler
  FILE* pFpBs = NULL;
#endif
#if defined(COMPARE_DATA)
  //For getting the golden file handle
  FILE* fpGolden = NULL;
#endif
#if defined ( STICK_STREAM_SIZE )
  FILE* fTrackStream = fopen ("coding_size.stream", "wb");;
#endif
  SFilesSet fs;
  // for configuration file
  CReadConfig cRdCfg;
  int iParsedNum = 2;

  memset (&sFbi, 0, sizeof (SFrameBSInfo));
  memset (&sSvcParam, 0, sizeof (SWelsSvcCodingParam));

  sSvcParam.iInputCsp	= videoFormatI420;	// I420 in default
  sSvcParam.sDependencyLayers[0].uiProfileIdc	= PRO_BASELINE;
//	svc_cfg->sDependencyLayers[0].frext_mode	= 0;

  // for configuration file
  cRdCfg.Openf (argv[1]);
  if (!cRdCfg.ExistFile()) {
    fprintf (stderr, "Specified file: %s not exist, maybe invalid path or parameter settting.\n",
             cRdCfg.GetFileName().c_str());
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

  iRet = ParseConfig (cRdCfg, sSvcParam, fs);
  if (iRet) {
    fprintf (stderr, "parse svc parameter config file failed.\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

  if (ParseCommandLine (argc - iParsedNum, argv + iParsedNum, sSvcParam, fs) != 0) {
    printf ("parse pCommand line failed\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }

  iTotalFrameMax = (int32_t)sSvcParam.uiFrameToBeCoded;
  sSvcParam.SUsedPicRect.iLeft = 0;
  sSvcParam.SUsedPicRect.iTop = 0;
//	sSvcParam.max_pic_width	=
  sSvcParam.iActualPicWidth =
    sSvcParam.SUsedPicRect.iWidth = sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iFrameWidth;
//	pSvcParam.max_pic_height	=
  sSvcParam.iActualPicHeight =
    sSvcParam.SUsedPicRect.iHeight = sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iFrameHeight;

  sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iFrameWidth =
    WELS_ALIGN(sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iActualWidth, MB_WIDTH_LUMA);
  sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iFrameHeight =
    WELS_ALIGN(sSvcParam.sDependencyLayers[sSvcParam.iNumDependencyLayer - 1].iActualHeight, MB_HEIGHT_LUMA);

  if (cmResultSuccess != pPtrEnc->Initialize2 ((void*)&sSvcParam, INIT_TYPE_CONFIG_BASED)) {	// SVC encoder initialization
    fprintf (stderr, "SVC encoder Initialize failed\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
#if (defined(RUN_SIMULATOR) || defined(_WIN32)||defined(_MACH_PLATFORM) || (defined(__GNUC__)))
  // Inactive with sink with output file handler
  if (fs.strBsFile.length() > 0) {
    pFpBs = fopen (fs.strBsFile.c_str(), "wb");
    if (pFpBs == NULL) {
      fprintf (stderr, "Can not open file (%s) to write bitstream!\n", fs.strBsFile.c_str());
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }
  }
#endif

#if defined(COMPARE_DATA)
  //For getting the golden file handle
  if ((fpGolden = fopen (argv[3], "rb")) == NULL) {
    fprintf (stderr, "Unable to open golden sequence file, check corresponding path!\n");
    iRet = 1;
    goto INSIDE_MEM_FREE;
  }
#endif

  pSrcPicList = new SSourcePicture * [sSvcParam.iNumDependencyLayer];
  while (iDlayerIdx < sSvcParam.iNumDependencyLayer) {
    SDLayerParam* pDLayer = &sSvcParam.sDependencyLayers[iDlayerIdx];
    const int kiPicResSize = pDLayer->iActualWidth * pDLayer->iActualHeight;
    SSourcePicture* pSrcPic = new SSourcePicture;
    if (pSrcPic == NULL) {
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }
    memset (pSrcPic, 0, sizeof (SSourcePicture));

    pYUV[iDlayerIdx] = new uint8_t [ (3 * kiPicResSize) >> 1];
    if (pYUV[iDlayerIdx] == NULL) {
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }

    pSrcPic->iColorFormat = videoFormatI420;
    pSrcPic->iPicWidth = pDLayer->iActualWidth;
    pSrcPic->iPicHeight = pDLayer->iActualHeight;
    pSrcPic->iStride[0] = pDLayer->iActualWidth;
    pSrcPic->iStride[1] = pSrcPic->iStride[2] = pDLayer->iActualWidth >> 1;

    pSrcPicList[iDlayerIdx] = pSrcPic;

    pFileYUV[iDlayerIdx]	= fopen (fs.sSpatialLayers[iDlayerIdx].strSeqFile.c_str(), "rb");
    if (pFileYUV[iDlayerIdx] != NULL) {
      if (!fseek (pFileYUV[iDlayerIdx], 0, SEEK_END)) {
        int64_t i_size = ftell (pFileYUV[iDlayerIdx]);
        fseek (pFileYUV[iDlayerIdx], 0, SEEK_SET);
        iTotalFrameMax = WELS_MAX ((int32_t) (i_size / ((3 * kiPicResSize) >> 1)), iTotalFrameMax);
      }
    } else {
      fprintf (stderr, "Unable to open source sequence file (%s), check corresponding path!\n",
               fs.sSpatialLayers[iDlayerIdx].strSeqFile.c_str());
      iRet = 1;
      goto INSIDE_MEM_FREE;
    }

    ++ iDlayerIdx;
  }

  iFrameIdx = 0;
  while (iFrameIdx < iTotalFrameMax && (((int32_t)sSvcParam.uiFrameToBeCoded <= 0)
                                        || (iFrameIdx < (int32_t)sSvcParam.uiFrameToBeCoded))) {
    bool_t bOnePicAvailableAtLeast = false;
    bool_t bSomeSpatialUnavailable	  = false;

#ifdef ONLY_ENC_FRAMES_NUM
    // Only encoded some limited frames here
    if (iActualFrameEncodedCount >= ONLY_ENC_FRAMES_NUM) {
      break;
    }
#endif//ONLY_ENC_FRAMES_NUM

    iDlayerIdx = 0;
    int  nSpatialLayerNum = 0;
    while (iDlayerIdx < sSvcParam.iNumDependencyLayer) {
      SDLayerParam* pDLayer = &sSvcParam.sDependencyLayers[iDlayerIdx];
      const int kiPicResSize = ((pDLayer->iActualWidth * pDLayer->iActualHeight) * 3) >> 1;
      uint32_t uiSkipIdx = (1 << pDLayer->iTemporalResolution);

      bool_t bCanBeRead = false;

      if (iFrameIdx % uiSkipIdx == 0) {	// such layer is enabled to encode indeed
        bCanBeRead = (fread (pYUV[iDlayerIdx], 1, kiPicResSize, pFileYUV[iDlayerIdx]) == kiPicResSize);

        if (bCanBeRead) {
          bOnePicAvailableAtLeast	= true;

          pSrcPicList[nSpatialLayerNum]->pData[0] = pYUV[iDlayerIdx];
          pSrcPicList[nSpatialLayerNum]->pData[1] = pSrcPicList[nSpatialLayerNum]->pData[0] +
              (pDLayer->iActualWidth * pDLayer->iActualHeight);
          pSrcPicList[nSpatialLayerNum]->pData[2] = pSrcPicList[nSpatialLayerNum]->pData[1] +
              ((pDLayer->iActualWidth * pDLayer->iActualHeight) >> 2);

          pSrcPicList[nSpatialLayerNum]->iPicWidth = pDLayer->iActualWidth;
          pSrcPicList[nSpatialLayerNum]->iPicHeight = pDLayer->iActualHeight;
          pSrcPicList[nSpatialLayerNum]->iStride[0] = pDLayer->iActualWidth;
          pSrcPicList[nSpatialLayerNum]->iStride[1] = pSrcPicList[nSpatialLayerNum]->iStride[2]
              = pDLayer->iActualWidth >> 1;

          ++ nSpatialLayerNum;
        } else {	// file end while reading
          bSomeSpatialUnavailable = true;
          break;
        }
      } else {

      }

      ++ iDlayerIdx;
    }

    if (bSomeSpatialUnavailable)
      break;

    if (!bOnePicAvailableAtLeast) {
      ++ iFrameIdx;
      continue;
    }

    // To encoder this frame
    iStart	= WelsTime();
    int iEncFrames = pPtrEnc->EncodeFrame2 (const_cast<const SSourcePicture**> (pSrcPicList), nSpatialLayerNum, &sFbi);
    iTotal += WelsTime() - iStart;

    // fixed issue in case dismatch source picture introduced by frame skipped, 1/12/2010
    if (videoFrameTypeSkip == iEncFrames) {
      continue;
    }

    if (iEncFrames != videoFrameTypeInvalid && iEncFrames != videoFrameTypeSkip) {
      int iLayer = 0;
      int iFrameSize = 0;
      while (iLayer < sFbi.iLayerNum) {
        SLayerBSInfo* pLayerBsInfo = &sFbi.sLayerInfo[iLayer];
        if (pLayerBsInfo != NULL) {
          int iLayerSize = 0;
          int iNalIdx = pLayerBsInfo->iNalCount - 1;
          do {
            iLayerSize += pLayerBsInfo->iNalLengthInByte[iNalIdx];
            -- iNalIdx;
          } while (iNalIdx >= 0);
#if defined(COMPARE_DATA)
          //Comparing the result of encoder with golden pData
          {
            unsigned char* pUCArry = new unsigned char [iLayerSize];

            fread (pUCArry, 1, iLayerSize, fpGolden);

            for (int w = 0; w < iLayerSize; w++) {
              if (pUCArry[w] != pLayerBsInfo->pBsBuf[w]) {
                fprintf (stderr, "error @frame%d/layer%d/byte%d!!!!!!!!!!!!!!!!!!!!!!!!\n", iFrameIdx, iLayer, w);
                //fprintf(stderr, "%x - %x\n", pUCArry[w], pLayerBsInfo->pBsBuf[w]);
                break;
              }
            }
            fprintf (stderr, "frame%d/layer%d comparation completed!\n", iFrameIdx, iLayer);

            delete [] pUCArry;
          }
#endif
#if (defined(RUN_SIMULATOR) || defined(_WIN32)||defined(_MACH_PLATFORM) || (defined(__GNUC__)))
          fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs);	// write pure bit stream into file
#endif
          iFrameSize += iLayerSize;
        }
        ++ iLayer;
      }
#if defined (STICK_STREAM_SIZE)
      if (fTrackStream) {
        fwrite (&iFrameSize, 1, sizeof (int), fTrackStream);
      }
#endif//STICK_STREAM_SIZE
      ++ iActualFrameEncodedCount;	// excluding skipped frame time
    } else {
      fprintf (stderr, "EncodeFrame(), ret: %d, frame index: %d.\n", iEncFrames, iFrameIdx);
    }

    ++ iFrameIdx;
  }

  if (iActualFrameEncodedCount > 0) {
    double dElapsed = iTotal / 1e6;
    printf ("Width:		%d\nHeight:		%d\nFrames:		%d\nencode time:	%f sec\nFPS:		%f fps\n",
            sSvcParam.iActualPicWidth, sSvcParam.iActualPicHeight,
            iActualFrameEncodedCount, dElapsed, (iActualFrameEncodedCount * 1.0) / dElapsed);
  }

INSIDE_MEM_FREE: {
#if (defined(RUN_SIMULATOR) || defined(_WIN32)||defined(_MACH_PLATFORM) || (defined(__GNUC__)))
    if (pFpBs) {
      fclose (pFpBs);
      pFpBs = NULL;
    }
#endif
#if defined (STICK_STREAM_SIZE)
    if (fTrackStream) {
      fclose (fTrackStream);
      fTrackStream = NULL;
    }
#endif
#if defined (COMPARE_DATA)
    if (fpGolden) {
      fclose (fpGolden);
      fpGolden = NULL;
    }
#endif
    // Destruction memory introduced in this routine
    iDlayerIdx = 0;
    while (iDlayerIdx < sSvcParam.iNumDependencyLayer) {
      if (pFileYUV[iDlayerIdx] != NULL) {
        fclose (pFileYUV[iDlayerIdx]);
        pFileYUV[iDlayerIdx] = NULL;
      }
      ++ iDlayerIdx;
    }

    if (pSrcPicList) {
      for (int32_t i = 0; i < sSvcParam.iNumDependencyLayer; i++) {
        if (pSrcPicList[i]) {
          delete pSrcPicList[i];
          pSrcPicList[i] = NULL;
        }
      }
      delete []pSrcPicList;
      pSrcPicList = NULL;
    }

    for (int32_t i = 0; i < MAX_DEPENDENCY_LAYER; i++) {
      if (pYUV[i]) {
        delete [] pYUV[i];
        pYUV[i] = NULL;
      }
    }
  }

  return iRet;
}

//  Merge from Heifei's Wonder.  Lock process to a single core
void LockToSingleCore() {
#ifdef HAVE_PROCESS_AFFINITY
  //for 2005 compiler, change "DWORD" to "DWORD_PTR"
  ULONG_PTR ProcessAffMask = 0, SystemAffMask = 0;
  HANDLE hProcess = GetCurrentProcess();

  GetProcessAffinityMask (hProcess, &ProcessAffMask, &SystemAffMask);
  if (ProcessAffMask > 1) {
    // more than one CPU core available. Fix to only one:
    if (ProcessAffMask & 2) {
      ProcessAffMask = 2;
    } else {
      ProcessAffMask = 1;
    }
    // Lock process to a single CPU core
    SetProcessAffinityMask (hProcess, ProcessAffMask);
  }

  // set high priority to avoid interrupts during test
  SetPriorityClass (hProcess, REALTIME_PRIORITY_CLASS);
#endif
  return ;
}

long CreateSVCEncHandle (ISVCEncoder** ppEncoder) {
  long ret = 0;
  ret = CreateSVCEncoder (ppEncoder);
  return ret;
}

void DestroySVCEncHandle (ISVCEncoder* pEncoder) {
  if (pEncoder) {
    DestroySVCEncoder (pEncoder);

  }
}

/****************************************************************************
 * main:
 ****************************************************************************/
int main (int argc, char** argv)
{
  ISVCEncoder* pSVCEncoder	= NULL;
  FILE* pFileOut					= NULL;
  FILE* pFileIn					= NULL;
  int iRet					= 0;

#ifdef _MSC_VER
  _setmode (_fileno (stdin), _O_BINARY);  /* thanks to Marcoss Morais <morais at dee.ufcg.edu.br> */
  _setmode (_fileno (stdout), _O_BINARY);

  // remove the LOCK_TO_SINGLE_CORE micro, user need to enable it with manual
  // LockToSingleCore();
#endif

  /* Control-C handler */
  signal (SIGINT, SigIntHandler);

  iRet = CreateSVCEncHandle (&pSVCEncoder);
  if (iRet) {
    cout << "CreateSVCEncoder() failed!!" << endl;
    goto exit;
  }

  if (argc < 2) {
    goto exit;
  } else {
    if (!strstr (argv[1], ".cfg")) { // check configuration type (like .cfg?)
      if (argc > 2) {
        iRet = ProcessEncodingSvcWithParam (pSVCEncoder, argc, argv);
        if (iRet != 0)
          goto exit;
      } else if (argc == 2 && ! strcmp (argv[1], "-h"))
        PrintHelp();
      else {
        cout << "You specified pCommand is invalid!!" << endl;
        goto exit;
      }
    } else {
      iRet = ProcessEncodingSvcWithConfig (pSVCEncoder, argc, argv);
      if (iRet > 0)
        goto exit;
    }
  }

  DestroySVCEncHandle (pSVCEncoder);
  return 0;

exit:
  DestroySVCEncHandle (pSVCEncoder);
  PrintHelp();
  return 1;
}
