| /*! |
| * \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. |
| * |
| */ |
| |
| #define _CRT_SECURE_NO_WARNINGS |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #if defined (ANDROID_NDK) |
| #include <android/log.h> |
| #endif |
| #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 |
| |
| #if defined (WINDOWS_PHONE) |
| float g_fFPS = 0.0; |
| double g_dEncoderTime = 0.0; |
| int g_iEncodedFrame = 0; |
| #endif |
| |
| #if defined (ANDROID_NDK) |
| #define LOG_TAG "welsenc" |
| #define LOGI(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) |
| #define printf(...) LOGI(__VA_ARGS__) |
| #define fprintf(a, ...) LOGI(__VA_ARGS__) |
| #endif |
| //#define STICK_STREAM_SIZE |
| |
| #include "measure_time.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 "mt_defs.h" |
| #include "WelsThreadLib.h" |
| |
| #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 */ |
| |
| #if defined(__linux__) || defined(__unix__) |
| #define _FILE_OFFSET_BITS 64 |
| #endif |
| |
| #include <iostream> |
| using namespace std; |
| using namespace WelsEnc; |
| |
| /* |
| * Layer Context |
| */ |
| typedef struct LayerpEncCtx_s { |
| int32_t iDLayerQp; |
| SSliceArgument sSliceArgument; |
| } SLayerPEncCtx; |
| |
| typedef struct tagFilesSet { |
| string strBsFile; |
| string strSeqFile; // for cmd lines |
| string strLayerCfgFile[MAX_DEPENDENCY_LAYER]; |
| char sRecFileName[MAX_DEPENDENCY_LAYER][MAX_FNAME_LEN]; |
| uint32_t uiFrameToBeCoded; |
| bool bEnableMultiBsFile; |
| |
| tagFilesSet() { |
| uiFrameToBeCoded = 0; |
| bEnableMultiBsFile = false; |
| for (int i = 0; i < MAX_DEPENDENCY_LAYER; i++) |
| sRecFileName[i][0] = '\0'; |
| } |
| } SFilesSet; |
| |
| |
| |
| /* Ctrl-C handler */ |
| static int g_iCtrlC = 0; |
| static void SigIntHandler (int a) { |
| g_iCtrlC = 1; |
| } |
| static int g_LevelSetting = WELS_LOG_ERROR; |
| |
| int ParseLayerConfig (CReadConfig& cRdLayerCfg, const int iLayer, SEncParamExt& pSvcParam, SFilesSet& sFileSet) { |
| if (!cRdLayerCfg.ExistFile()) { |
| fprintf (stderr, "Unabled to open layer #%d configuration file: %s.\n", iLayer, cRdLayerCfg.GetFileName().c_str()); |
| return 1; |
| } |
| |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| int iLeftTargetBitrate = (pSvcParam.iRCMode != RC_OFF_MODE) ? pSvcParam.iTargetBitrate : 0; |
| SLayerPEncCtx sLayerCtx; |
| memset (&sLayerCtx, 0, sizeof (SLayerPEncCtx)); |
| |
| string strTag[4]; |
| string str_ ("SlicesAssign"); |
| const int kiSize = (int)str_.size(); |
| |
| while (!cRdLayerCfg.EndOfFile()) { |
| long iLayerRd = cRdLayerCfg.ReadLine (&strTag[0]); |
| if (iLayerRd > 0) { |
| if (strTag[0].empty()) |
| continue; |
| if (strTag[0].compare ("FrameWidth") == 0) { |
| pDLayer->iVideoWidth = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("FrameHeight") == 0) { |
| pDLayer->iVideoHeight = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("FrameRateOut") == 0) { |
| pDLayer->fFrameRate = (float)atof (strTag[1].c_str()); |
| } else if (strTag[0].compare ("ReconFile") == 0) { |
| const unsigned int kiLen = (unsigned int)strTag[1].length(); |
| if (kiLen >= sizeof (sFileSet.sRecFileName[iLayer])) |
| return -1; |
| sFileSet.sRecFileName[iLayer][kiLen] = '\0'; |
| strncpy (sFileSet.sRecFileName[iLayer], strTag[1].c_str(), kiLen); // confirmed_safe_unsafe_usage |
| } else if (strTag[0].compare ("ProfileIdc") == 0) { |
| pDLayer->uiProfileIdc = (EProfileIdc)atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("FRExt") == 0) { |
| // pDLayer->frext_mode = (bool)atoi(strTag[1].c_str()); |
| } else if (strTag[0].compare ("SpatialBitrate") == 0) { |
| pDLayer->iSpatialBitrate = 1000 * atoi (strTag[1].c_str()); |
| if (pSvcParam.iRCMode != RC_OFF_MODE) { |
| if (pDLayer->iSpatialBitrate <= 0) { |
| fprintf (stderr, "Invalid spatial bitrate(%d) in dependency layer #%d.\n", pDLayer->iSpatialBitrate, iLayer); |
| return -1; |
| } |
| if (pDLayer->iSpatialBitrate > iLeftTargetBitrate) { |
| fprintf (stderr, "Invalid spatial(#%d) bitrate(%d) setting due to unavailable left(%d)!\n", iLayer, |
| pDLayer->iSpatialBitrate, iLeftTargetBitrate); |
| return -1; |
| } |
| iLeftTargetBitrate -= pDLayer->iSpatialBitrate; |
| } |
| } else if (strTag[0].compare ("MaxSpatialBitrate") == 0) { |
| pDLayer->iMaxSpatialBitrate = 1000 * atoi (strTag[1].c_str()); |
| if (pSvcParam.iRCMode != RC_OFF_MODE) { |
| if (pDLayer->iMaxSpatialBitrate < 0) { |
| fprintf (stderr, "Invalid max spatial bitrate(%d) in dependency layer #%d.\n", pDLayer->iMaxSpatialBitrate, iLayer); |
| return -1; |
| } |
| if (pDLayer->iMaxSpatialBitrate > 0 && pDLayer->iMaxSpatialBitrate < pDLayer->iSpatialBitrate) { |
| fprintf (stderr, "Invalid max spatial(#%d) bitrate(%d) setting::: < layerBitrate(%d)!\n", iLayer, |
| pDLayer->iMaxSpatialBitrate, pDLayer->iSpatialBitrate); |
| return -1; |
| } |
| } |
| } else if (strTag[0].compare ("InitialQP") == 0) { |
| sLayerCtx.iDLayerQp = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("SliceMode") == 0) { |
| sLayerCtx.sSliceArgument.uiSliceMode = (SliceModeEnum)atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("SliceSize") == 0) { //SM_SIZELIMITED_SLICE |
| sLayerCtx.sSliceArgument.uiSliceSizeConstraint = atoi (strTag[1].c_str()); |
| continue; |
| } else if (strTag[0].compare ("SliceNum") == 0) { |
| sLayerCtx.sSliceArgument.uiSliceNum = 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.sSliceArgument.uiSliceMbNum[uiSliceIdx] = atoi (strTag[1].c_str()); |
| } |
| } |
| } |
| pDLayer->iDLayerQp = sLayerCtx.iDLayerQp; |
| pDLayer->sSliceArgument.uiSliceMode = sLayerCtx.sSliceArgument.uiSliceMode; |
| |
| memcpy (&pDLayer->sSliceArgument, &sLayerCtx.sSliceArgument, sizeof (SSliceArgument)); // confirmed_safe_unsafe_usage |
| memcpy (&pDLayer->sSliceArgument.uiSliceMbNum[0], &sLayerCtx.sSliceArgument.uiSliceMbNum[0], |
| sizeof (sLayerCtx.sSliceArgument.uiSliceMbNum)); // confirmed_safe_unsafe_usage |
| |
| return 0; |
| } |
| int ParseConfig (CReadConfig& cRdCfg, SSourcePicture* pSrcPic, SEncParamExt& pSvcParam, SFilesSet& sFileSet) { |
| string strTag[4]; |
| int32_t iRet = 0; |
| int8_t iLayerCount = 0; |
| |
| while (!cRdCfg.EndOfFile()) { |
| long iRd = cRdCfg.ReadLine (&strTag[0]); |
| if (iRd > 0) { |
| if (strTag[0].empty()) |
| continue; |
| |
| if (strTag[0].compare ("UsageType") == 0) { |
| pSvcParam.iUsageType = (EUsageType)atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("SimulcastAVC") == 0) { |
| pSvcParam.bSimulcastAVC = atoi (strTag[1].c_str()) ? true : false; |
| } else if (strTag[0].compare ("SourceWidth") == 0) { |
| pSrcPic->iPicWidth = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("SourceHeight") == 0) { |
| pSrcPic->iPicHeight = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("InputFile") == 0) { |
| if (strTag[1].length() > 0) |
| sFileSet.strSeqFile = strTag[1]; |
| } else 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) { |
| sFileSet.uiFrameToBeCoded = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("TemporalLayerNum") == 0) { |
| pSvcParam.iTemporalLayerNum = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("IntraPeriod") == 0) { |
| pSvcParam.uiIntraPeriod = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("MaxNalSize") == 0) { |
| pSvcParam.uiMaxNalSize = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("SpsPpsIDStrategy") == 0) { |
| int32_t iValue = atoi (strTag[1].c_str()); |
| switch (iValue) { |
| case 0: |
| pSvcParam.eSpsPpsIdStrategy = CONSTANT_ID; |
| break; |
| case 0x01: |
| pSvcParam.eSpsPpsIdStrategy = INCREASING_ID; |
| break; |
| case 0x02: |
| pSvcParam.eSpsPpsIdStrategy = SPS_LISTING; |
| break; |
| case 0x03: |
| pSvcParam.eSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; |
| break; |
| case 0x06: |
| pSvcParam.eSpsPpsIdStrategy = SPS_PPS_LISTING; |
| break; |
| default: |
| pSvcParam.eSpsPpsIdStrategy = CONSTANT_ID; |
| break; |
| } |
| } else if (strTag[0].compare ("EnableMultiBsFile") == 0) { |
| sFileSet.bEnableMultiBsFile = 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 ("EntropyCodingModeFlag") == 0) { |
| pSvcParam.iEntropyCodingModeFlag = (atoi (strTag[1].c_str()) != 0); |
| } else if (strTag[0].compare ("ComplexityMode") == 0) { |
| pSvcParam.iComplexityMode = (ECOMPLEXITY_MODE) (atoi (strTag[1].c_str())); |
| } 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 ("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 ("UseLoadBalancing") == 0) { |
| pSvcParam.bUseLoadBalancing = (atoi (strTag[1].c_str())) ? true : false; |
| } else if (strTag[0].compare ("RCMode") == 0) { |
| pSvcParam.iRCMode = (RC_MODES) atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("TargetBitrate") == 0) { |
| pSvcParam.iTargetBitrate = 1000 * atoi (strTag[1].c_str()); |
| if ((pSvcParam.iRCMode != RC_OFF_MODE) && pSvcParam.iTargetBitrate <= 0) { |
| fprintf (stderr, "Invalid target bitrate setting due to RC enabled. Check TargetBitrate field please!\n"); |
| return 1; |
| } |
| } else if (strTag[0].compare ("MaxOverallBitrate") == 0) { |
| pSvcParam.iMaxBitrate = 1000 * atoi (strTag[1].c_str()); |
| if ((pSvcParam.iRCMode != RC_OFF_MODE) && pSvcParam.iMaxBitrate < 0) { |
| fprintf (stderr, "Invalid max overall bitrate setting due to RC enabled. Check MaxOverallBitrate field please!\n"); |
| return 1; |
| } |
| } else if (strTag[0].compare ("MaxQp") == 0) { |
| pSvcParam.iMaxQp = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("MinQp") == 0) { |
| pSvcParam.iMinQp = atoi (strTag[1].c_str()); |
| } 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 ("LongTermReferenceNumber") == 0) { |
| pSvcParam.iLTRRefNum = atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("LtrMarkPeriod") == 0) { |
| pSvcParam.iLtrMarkPeriod = (uint32_t)atoi (strTag[1].c_str()); |
| } else if (strTag[0].compare ("LosslessLink") == 0) { |
| pSvcParam.bIsLosslessLink = atoi (strTag[1].c_str()) ? true : false; |
| } else if (strTag[0].compare ("NumLayers") == 0) { |
| pSvcParam.iSpatialLayerNum = (int8_t)atoi (strTag[1].c_str()); |
| if (pSvcParam.iSpatialLayerNum > MAX_DEPENDENCY_LAYER || pSvcParam.iSpatialLayerNum <= 0) { |
| fprintf (stderr, "Invalid parameter in iSpatialLayerNum: %d.\n", pSvcParam.iSpatialLayerNum); |
| iRet = 1; |
| break; |
| } |
| } else if (strTag[0].compare ("LayerCfg") == 0) { |
| if (strTag[1].length() > 0) |
| sFileSet.strLayerCfgFile[iLayerCount] = 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.iSpatialLayerNum, iLayerCount); |
| if (pSvcParam.iSpatialLayerNum > |
| kiActualLayerNum) { // fixed number of dependency layer due to parameter error in settings |
| pSvcParam.iSpatialLayerNum = kiActualLayerNum; |
| } |
| |
| assert (kiActualLayerNum <= MAX_DEPENDENCY_LAYER); |
| |
| for (int8_t iLayer = 0; iLayer < kiActualLayerNum; ++ iLayer) { |
| CReadConfig cRdLayerCfg (sFileSet.strLayerCfgFile[iLayer]); |
| if (-1 == ParseLayerConfig (cRdLayerCfg, iLayer, pSvcParam, sFileSet)) { |
| iRet = 1; |
| break; |
| } |
| } |
| |
| return iRet; |
| } |
| |
| 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 (" -org Original file, example: -org src.yuv\n"); |
| printf (" -sw the source width\n"); |
| printf (" -sh the source height\n"); |
| printf (" -utype usage type\n"); |
| printf (" -savc simulcast avc\n"); |
| printf (" -frms Number of total frames to be encoded\n"); |
| printf (" -frin input frame rate\n"); |
| printf (" -numtl Temporal layer number (default: 1)\n"); |
| printf (" -iper Intra period (default: -1) : must be a power of 2 of GOP size (or -1)\n"); |
| printf (" -nalsize the Maximum NAL size. which should be larger than the each layer slicesize when slice mode equals to SM_SIZELIMITED_SLICE\n"); |
| printf (" -spsid SPS/PPS id strategy: 0:const, 1: increase, 2: sps list, 3: sps list and pps increase, 4: sps/pps list\n"); |
| printf (" -cabac Entropy coding mode(0:cavlc 1:cabac \n"); |
| printf (" -complexity Complexity mode (default: 0),0: low complexity, 1: medium complexity, 2: high complexity\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 (" -ltrnum Control the number of long term reference((1-4):screen LTR,(1-2):video LTR \n"); |
| printf (" -ltrper Control the long term reference marking period \n"); |
| printf (" -threadIdc 0: auto(dynamic imp. internal encoder); 1: multiple threads imp. disabled; > 1: count number of threads \n"); |
| printf (" -loadbalancing 0: turn off loadbalancing between slices when multi-threading available; 1: (default value) turn on loadbalancing between slices when multi-threading available\n"); |
| printf (" -deblockIdc Loop filter idc (0: on, 1: off, \n"); |
| printf (" -alphaOffset AlphaOffset(-6..+6): valid range \n"); |
| printf (" -betaOffset BetaOffset (-6..+6): valid range\n"); |
| printf (" -rc rate control mode: -1-rc off; 0-quality mode; 1-bitrate mode; 2: buffer based mode,can't control bitrate; 3: bitrate mode based on timestamp input;\n"); |
| printf (" -tarb Overall target bitrate\n"); |
| printf (" -maxbrTotal Overall max bitrate\n"); |
| printf (" -maxqp Maximum Qp (default: %d, or for screen content usage: %d)\n", QP_MAX_VALUE, MAX_SCREEN_QP); |
| printf (" -minqp Minimum Qp (default: %d, or for screen content usage: %d)\n", QP_MIN_VALUE, MIN_SCREEN_QP); |
| 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 (" -lconfig (Layer) (spatial layer configure file)\n"); |
| printf (" -drec (Layer) (reconstruction file);example: -drec 0 rec.yuv. Setting the reconstruction file, this will only functioning when dumping reconstruction is enabled\n"); |
| printf (" -dprofile (Layer) (layer profile);example: -dprofile 0 66. Setting the layer profile, this should be the same for all layers\n"); |
| printf (" -dw (Layer) (output width)\n"); |
| printf (" -dh (Layer) (output height)\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 (" -lmaxb (Layer) (spatial layer max 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 ; -slcmbnum: set the first slice mb num under some slice modes) \n"); |
| printf (" -trace (Level)\n"); |
| printf (" -fixrc Enable fix RC overshoot(default: 1)\n"); |
| printf ("\n"); |
| } |
| |
| int ParseCommandLine (int argc, char** argv, SSourcePicture* pSrcPic, SEncParamExt& pSvcParam, SFilesSet& sFileSet) { |
| char* pCommand = NULL; |
| SLayerPEncCtx sLayerCtx[MAX_SPATIAL_LAYER_NUM]; |
| int n = 0; |
| string str_ ("SlicesAssign"); |
| |
| while (n < argc) { |
| pCommand = argv[n++]; |
| |
| if (!strcmp (pCommand, "-bf") && (n < argc)) |
| sFileSet.strBsFile.assign (argv[n++]); |
| else if (!strcmp (pCommand, "-utype") && (n < argc)) |
| pSvcParam.iUsageType = (EUsageType)atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-savc") && (n < argc)) |
| pSvcParam.bSimulcastAVC = atoi (argv[n++]) ? true : false; |
| |
| else if (!strcmp (pCommand, "-org") && (n < argc)) |
| sFileSet.strSeqFile.assign (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-sw") && (n < argc))//source width |
| pSrcPic->iPicWidth = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-sh") && (n < argc))//source height |
| pSrcPic->iPicHeight = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-frms") && (n < argc)) |
| sFileSet.uiFrameToBeCoded = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-frin") && (n < argc)) |
| pSvcParam.fMaxFrameRate = (float) atof (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-numtl") && (n < argc)) |
| pSvcParam.iTemporalLayerNum = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-mfile") && (n < argc)) |
| sFileSet.bEnableMultiBsFile = atoi (argv[n++]) ? true : false; |
| |
| else if (!strcmp (pCommand, "-iper") && (n < argc)) |
| pSvcParam.uiIntraPeriod = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-nalsize") && (n < argc)) |
| pSvcParam.uiMaxNalSize = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-spsid") && (n < argc)) { |
| int32_t iValue = atoi (argv[n++]); |
| switch (iValue) { |
| case 0: |
| pSvcParam.eSpsPpsIdStrategy = CONSTANT_ID; |
| break; |
| case 0x01: |
| pSvcParam.eSpsPpsIdStrategy = INCREASING_ID; |
| break; |
| case 0x02: |
| pSvcParam.eSpsPpsIdStrategy = SPS_LISTING; |
| break; |
| case 0x03: |
| pSvcParam.eSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; |
| break; |
| case 0x06: |
| pSvcParam.eSpsPpsIdStrategy = SPS_PPS_LISTING; |
| break; |
| default: |
| pSvcParam.eSpsPpsIdStrategy = CONSTANT_ID; |
| break; |
| } |
| } else if (!strcmp (pCommand, "-cabac") && (n < argc)) |
| pSvcParam.iEntropyCodingModeFlag = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-complexity") && (n < argc)) |
| pSvcParam.iComplexityMode = (ECOMPLEXITY_MODE)atoi (argv[n++]); |
| |
| 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, "-fixrc") && (n < argc)) |
| pSvcParam.bFixRCOverShoot = atoi (argv[n++]) ? true : false; |
| |
| else if (!strcmp (pCommand, "-idrBitrateRatio") && (n < argc)) |
| pSvcParam.iIdrBitrateRatio = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-ltr") && (n < argc)) |
| pSvcParam.bEnableLongTermReference = atoi (argv[n++]) ? true : false; |
| |
| else if (!strcmp (pCommand, "-ltrnum") && (n < argc)) |
| pSvcParam.iLTRRefNum = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-ltrper") && (n < argc)) |
| pSvcParam.iLtrMarkPeriod = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-threadIdc") && (n < argc)) |
| pSvcParam.iMultipleThreadIdc = atoi (argv[n++]); |
| else if (!strcmp (pCommand, "-loadbalancing") && (n + 1 < argc)) { |
| pSvcParam.bUseLoadBalancing = (atoi (argv[n++])) ? true : false; |
| } else if (!strcmp (pCommand, "-deblockIdc") && (n < argc)) |
| pSvcParam.iLoopFilterDisableIdc = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-alphaOffset") && (n < argc)) |
| pSvcParam.iLoopFilterAlphaC0Offset = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-betaOffset") && (n < argc)) |
| pSvcParam.iLoopFilterBetaOffset = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-rc") && (n < argc)) |
| pSvcParam.iRCMode = static_cast<RC_MODES> (atoi (argv[n++])); |
| |
| else if (!strcmp (pCommand, "-trace") && (n < argc)) |
| g_LevelSetting = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-tarb") && (n < argc)) |
| pSvcParam.iTargetBitrate = 1000 * atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-maxbrTotal") && (n < argc)) |
| pSvcParam.iMaxBitrate = 1000 * atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-maxqp") && (n < argc)) |
| pSvcParam.iMaxQp = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-minqp") && (n < argc)) |
| pSvcParam.iMinQp = atoi (argv[n++]); |
| |
| else if (!strcmp (pCommand, "-numl") && (n < argc)) { |
| pSvcParam.iSpatialLayerNum = atoi (argv[n++]); |
| } else if (!strcmp (pCommand, "-lconfig") && (n < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| sFileSet.strLayerCfgFile[iLayer].assign (argv[n++]); |
| CReadConfig cRdLayerCfg (sFileSet.strLayerCfgFile[iLayer]); |
| if (-1 == ParseLayerConfig (cRdLayerCfg, iLayer, pSvcParam, sFileSet)) { |
| return 1; |
| } |
| } else if (!strcmp (pCommand, "-dprofile") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->uiProfileIdc = (EProfileIdc) atoi (argv[n++]); |
| } else if (!strcmp (pCommand, "-drec") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| const unsigned int iLen = (int) strlen (argv[n]); |
| if (iLen >= sizeof (sFileSet.sRecFileName[iLayer])) |
| return 1; |
| sFileSet.sRecFileName[iLayer][iLen] = '\0'; |
| strncpy (sFileSet.sRecFileName[iLayer], argv[n++], iLen); // confirmed_safe_unsafe_usage |
| } else if (!strcmp (pCommand, "-dw") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->iVideoWidth = atoi (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-dh") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->iVideoHeight = atoi (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-frout") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->fFrameRate = (float)atof (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-lqp") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[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++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->iSpatialBitrate = 1000 * atoi (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-lmaxb") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->iMaxSpatialBitrate = 1000 * atoi (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-slcmd") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| |
| switch (atoi (argv[n++])) { |
| case 0: |
| pDLayer->sSliceArgument.uiSliceMode = SM_SINGLE_SLICE; |
| break; |
| case 1: |
| pDLayer->sSliceArgument.uiSliceMode = SM_FIXEDSLCNUM_SLICE; |
| break; |
| case 2: |
| pDLayer->sSliceArgument.uiSliceMode = SM_RASTER_SLICE; |
| break; |
| case 3: |
| pDLayer->sSliceArgument.uiSliceMode = SM_SIZELIMITED_SLICE; |
| break; |
| default: |
| pDLayer->sSliceArgument.uiSliceMode = SM_RESERVED; |
| break; |
| } |
| } |
| |
| else if (!strcmp (pCommand, "-slcsize") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->sSliceArgument.uiSliceSizeConstraint = atoi (argv[n++]); |
| } |
| |
| else if (!strcmp (pCommand, "-slcnum") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->sSliceArgument.uiSliceNum = atoi (argv[n++]); |
| } else if (!strcmp (pCommand, "-slcmbnum") && (n + 1 < argc)) { |
| unsigned int iLayer = atoi (argv[n++]); |
| SSpatialLayerConfig* pDLayer = &pSvcParam.sSpatialLayers[iLayer]; |
| pDLayer->sSliceArgument.uiSliceMbNum[0] = atoi (argv[n++]); |
| } |
| } |
| return 0; |
| } |
| |
| |
| |
| int FillSpecificParameters (SEncParamExt& sParam) { |
| /* Test for temporal, spatial, SNR scalability */ |
| sParam.iUsageType = CAMERA_VIDEO_REAL_TIME; |
| sParam.fMaxFrameRate = 60.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.iMaxBitrate = UNSPECIFIED_BIT_RATE; |
| sParam.iRCMode = RC_QUALITY_MODE; // 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.uiIntraPeriod = 320; // period of Intra frame |
| sParam.eSpsPpsIdStrategy = INCREASING_ID; |
| sParam.bPrefixNalAddingCtrl = 0; |
| sParam.iComplexityMode = LOW_COMPLEXITY; |
| sParam.bSimulcastAVC = false; |
| sParam.bFixRCOverShoot = true; |
| sParam.iIdrBitrateRatio = IDR_BITRATE_RATIO * 100; |
| int iIndexLayer = 0; |
| sParam.sSpatialLayers[iIndexLayer].uiProfileIdc = PRO_BASELINE; |
| sParam.sSpatialLayers[iIndexLayer].iVideoWidth = 160; |
| sParam.sSpatialLayers[iIndexLayer].iVideoHeight = 90; |
| sParam.sSpatialLayers[iIndexLayer].fFrameRate = 7.5f; |
| sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate = 64000; |
| sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate = UNSPECIFIED_BIT_RATE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE; |
| |
| ++ iIndexLayer; |
| sParam.sSpatialLayers[iIndexLayer].uiProfileIdc = PRO_SCALABLE_BASELINE; |
| sParam.sSpatialLayers[iIndexLayer].iVideoWidth = 320; |
| sParam.sSpatialLayers[iIndexLayer].iVideoHeight = 180; |
| sParam.sSpatialLayers[iIndexLayer].fFrameRate = 15.0f; |
| sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate = 160000; |
| sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate = UNSPECIFIED_BIT_RATE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE; |
| |
| ++ iIndexLayer; |
| sParam.sSpatialLayers[iIndexLayer].uiProfileIdc = PRO_SCALABLE_BASELINE; |
| sParam.sSpatialLayers[iIndexLayer].iVideoWidth = 640; |
| sParam.sSpatialLayers[iIndexLayer].iVideoHeight = 360; |
| sParam.sSpatialLayers[iIndexLayer].fFrameRate = 30.0f; |
| sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate = 512000; |
| sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate = UNSPECIFIED_BIT_RATE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceNum = 1; |
| |
| ++ iIndexLayer; |
| sParam.sSpatialLayers[iIndexLayer].uiProfileIdc = PRO_SCALABLE_BASELINE; |
| sParam.sSpatialLayers[iIndexLayer].iVideoWidth = 1280; |
| sParam.sSpatialLayers[iIndexLayer].iVideoHeight = 720; |
| sParam.sSpatialLayers[iIndexLayer].fFrameRate = 30.0f; |
| sParam.sSpatialLayers[iIndexLayer].iSpatialBitrate = 1500000; |
| sParam.sSpatialLayers[iIndexLayer].iMaxSpatialBitrate = UNSPECIFIED_BIT_RATE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceMode = SM_SINGLE_SLICE; |
| sParam.sSpatialLayers[iIndexLayer].sSliceArgument.uiSliceNum = 1; |
| |
| 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.fMaxFrameRate = fMaxFr; |
| |
| return 0; |
| } |
| |
| int ProcessEncoding (ISVCEncoder* pPtrEnc, int argc, char** argv, bool bConfigFile) { |
| int iRet = 0; |
| |
| if (pPtrEnc == NULL) |
| return 1; |
| |
| SFrameBSInfo sFbi; |
| SEncParamExt sSvcParam; |
| int64_t iStart = 0, iTotal = 0; |
| |
| // Preparing encoding process |
| FILE* pFileYUV = NULL; |
| int32_t iActualFrameEncodedCount = 0; |
| int32_t iFrameIdx = 0; |
| int32_t iTotalFrameMax = -1; |
| uint8_t* pYUV = NULL; |
| SSourcePicture* pSrcPic = NULL; |
| uint32_t iSourceWidth, iSourceHeight, kiPicResSize; |
| // Inactive with sink with output file handler |
| FILE* pFpBs[4]; |
| pFpBs[0] = pFpBs[1] = pFpBs[2] = pFpBs[3] = NULL; |
| #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 = 1; |
| |
| memset (&sFbi, 0, sizeof (SFrameBSInfo)); |
| pPtrEnc->GetDefaultParams (&sSvcParam); |
| fs.bEnableMultiBsFile = false; |
| |
| FillSpecificParameters (sSvcParam); |
| pSrcPic = new SSourcePicture; |
| if (pSrcPic == NULL) { |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| //fill default pSrcPic |
| pSrcPic->iColorFormat = videoFormatI420; |
| pSrcPic->uiTimeStamp = 0; |
| |
| // if configure file exit, reading configure file firstly |
| if (bConfigFile) { |
| iParsedNum = 2; |
| 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, pSrcPic, 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, pSrcPic, sSvcParam, fs) != 0) { |
| printf ("parse pCommand line failed\n"); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| pPtrEnc->SetOption (ENCODER_OPTION_TRACE_LEVEL, &g_LevelSetting); |
| //finish reading the configurations |
| iSourceWidth = pSrcPic->iPicWidth; |
| iSourceHeight = pSrcPic->iPicHeight; |
| kiPicResSize = iSourceWidth * iSourceHeight * 3 >> 1; |
| |
| pYUV = new uint8_t [kiPicResSize]; |
| if (pYUV == NULL) { |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| |
| //update pSrcPic |
| pSrcPic->iStride[0] = iSourceWidth; |
| pSrcPic->iStride[1] = pSrcPic->iStride[2] = pSrcPic->iStride[0] >> 1; |
| |
| pSrcPic->pData[0] = pYUV; |
| pSrcPic->pData[1] = pSrcPic->pData[0] + (iSourceWidth * iSourceHeight); |
| pSrcPic->pData[2] = pSrcPic->pData[1] + (iSourceWidth * iSourceHeight >> 2); |
| |
| //update sSvcParam |
| sSvcParam.iPicWidth = 0; |
| sSvcParam.iPicHeight = 0; |
| for (int iLayer = 0; iLayer < sSvcParam.iSpatialLayerNum; iLayer++) { |
| SSpatialLayerConfig* pDLayer = &sSvcParam.sSpatialLayers[iLayer]; |
| sSvcParam.iPicWidth = WELS_MAX (sSvcParam.iPicWidth, pDLayer->iVideoWidth); |
| sSvcParam.iPicHeight = WELS_MAX (sSvcParam.iPicHeight, pDLayer->iVideoHeight); |
| } |
| //if target output resolution is not set, use the source size |
| sSvcParam.iPicWidth = (!sSvcParam.iPicWidth) ? iSourceWidth : sSvcParam.iPicWidth; |
| sSvcParam.iPicHeight = (!sSvcParam.iPicHeight) ? iSourceHeight : sSvcParam.iPicHeight; |
| |
| iTotalFrameMax = (int32_t)fs.uiFrameToBeCoded; |
| // sSvcParam.bSimulcastAVC = true; |
| if (cmResultSuccess != pPtrEnc->InitializeExt (&sSvcParam)) { // SVC encoder initialization |
| fprintf (stderr, "SVC encoder Initialize failed\n"); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| for (int iLayer = 0; iLayer < MAX_DEPENDENCY_LAYER; iLayer++) { |
| if (fs.sRecFileName[iLayer][0] != 0) { |
| SDumpLayer sDumpLayer; |
| sDumpLayer.iLayer = iLayer; |
| sDumpLayer.pFileName = fs.sRecFileName[iLayer]; |
| if (cmResultSuccess != pPtrEnc->SetOption (ENCODER_OPTION_DUMP_FILE, &sDumpLayer)) { |
| fprintf (stderr, "SetOption ENCODER_OPTION_DUMP_FILE failed!\n"); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| } |
| } |
| // Inactive with sink with output file handler |
| if (fs.strBsFile.length() > 0) { |
| bool bFileOpenErr = false; |
| if (sSvcParam.iSpatialLayerNum == 1 || fs.bEnableMultiBsFile == false) { |
| pFpBs[0] = fopen (fs.strBsFile.c_str(), "wb"); |
| bFileOpenErr = (pFpBs[0] == NULL); |
| } else { //enable multi bs file writing |
| string filename_layer; |
| string add_info[4] = {"_layer0", "_layer1", "_layer2", "_layer3"}; |
| string::size_type found = fs.strBsFile.find_last_of ('.'); |
| for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) { |
| filename_layer = fs.strBsFile.insert (found, add_info[i]); |
| pFpBs[i] = fopen (filename_layer.c_str(), "wb"); |
| fs.strBsFile = filename_layer.erase (found, 7); |
| bFileOpenErr |= (pFpBs[i] == NULL); |
| } |
| } |
| if (bFileOpenErr) { |
| fprintf (stderr, "Can not open file (%s) to write bitstream!\n", fs.strBsFile.c_str()); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| } else { |
| fprintf (stderr, "Don't set the proper bitstream filename!\n"); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| |
| #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 |
| |
| pFileYUV = fopen (fs.strSeqFile.c_str(), "rb"); |
| if (pFileYUV != NULL) { |
| #if defined(_WIN32) || defined(_WIN64) |
| #if _MSC_VER >= 1400 |
| if (!_fseeki64 (pFileYUV, 0, SEEK_END)) { |
| int64_t i_size = _ftelli64 (pFileYUV); |
| _fseeki64 (pFileYUV, 0, SEEK_SET); |
| iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax); |
| } |
| #else |
| if (!fseek (pFileYUV, 0, SEEK_END)) { |
| int64_t i_size = ftell (pFileYUV); |
| fseek (pFileYUV, 0, SEEK_SET); |
| iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax); |
| } |
| #endif |
| #else |
| if (!fseeko (pFileYUV, 0, SEEK_END)) { |
| int64_t i_size = ftello (pFileYUV); |
| fseeko (pFileYUV, 0, SEEK_SET); |
| iTotalFrameMax = WELS_MAX ((int32_t) (i_size / kiPicResSize), iTotalFrameMax); |
| } |
| #endif |
| } else { |
| fprintf (stderr, "Unable to open source sequence file (%s), check corresponding path!\n", |
| fs.strSeqFile.c_str()); |
| iRet = 1; |
| goto INSIDE_MEM_FREE; |
| } |
| |
| iFrameIdx = 0; |
| while (iFrameIdx < iTotalFrameMax && (((int32_t)fs.uiFrameToBeCoded <= 0) |
| || (iFrameIdx < (int32_t)fs.uiFrameToBeCoded))) { |
| |
| #ifdef ONLY_ENC_FRAMES_NUM |
| // Only encoded some limited frames here |
| if (iActualFrameEncodedCount >= ONLY_ENC_FRAMES_NUM) { |
| break; |
| } |
| #endif//ONLY_ENC_FRAMES_NUM |
| bool bCanBeRead = false; |
| bCanBeRead = (fread (pYUV, 1, kiPicResSize, pFileYUV) == kiPicResSize); |
| |
| if (!bCanBeRead) |
| break; |
| // To encoder this frame |
| iStart = WelsTime(); |
| pSrcPic->uiTimeStamp = WELS_ROUND (iFrameIdx * (1000 / sSvcParam.fMaxFrameRate)); |
| int iEncFrames = pPtrEnc->EncodeFrame (pSrcPic, &sFbi); |
| iTotal += WelsTime() - iStart; |
| ++ iFrameIdx; |
| if (videoFrameTypeSkip == sFbi.eFrameType) { |
| continue; |
| } |
| |
| if (iEncFrames == cmResultSuccess) { |
| int iLayer = 0; |
| #if defined (STICK_STREAM_SIZE) |
| int iFrameSize = 0; |
| #endif//STICK_STREAM_SIZE |
| while (iLayer < sFbi.iLayerNum) { |
| SLayerBSInfo* pLayerBsInfo = &sFbi.sLayerInfo[iLayer]; |
| if (pLayerBsInfo != NULL) { |
| int iLayerSize = 0; |
| int iNalIdx = pLayerBsInfo->iNalCount - 1; |
| do { |
| iLayerSize += pLayerBsInfo->pNalLengthInByte[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 (sSvcParam.iSpatialLayerNum == 1 || fs.bEnableMultiBsFile == false) |
| fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[0]); // write pure bit stream into file |
| else { //multi bs file write |
| if (pLayerBsInfo->uiSpatialId == 0) { |
| unsigned char five_bits = pLayerBsInfo->pBsBuf[4] & 0x1f; |
| if ((five_bits == 0x07) || (five_bits == 0x08)) { //sps or pps |
| for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) { |
| fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[i]); |
| } |
| } else { |
| fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[0]); |
| } |
| } else { |
| fwrite (pLayerBsInfo->pBsBuf, 1, iLayerSize, pFpBs[pLayerBsInfo->uiSpatialId]); |
| } |
| } |
| #if defined (STICK_STREAM_SIZE) |
| iFrameSize += iLayerSize; |
| #endif//STICK_STREAM_SIZE |
| } |
| ++ 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); |
| } |
| |
| } |
| |
| if (iActualFrameEncodedCount > 0) { |
| double dElapsed = iTotal / 1e6; |
| printf ("Width:\t\t%d\nHeight:\t\t%d\nFrames:\t\t%d\nencode time:\t%f sec\nFPS:\t\t%f fps\n", |
| sSvcParam.iPicWidth, sSvcParam.iPicHeight, |
| iActualFrameEncodedCount, dElapsed, (iActualFrameEncodedCount * 1.0) / dElapsed); |
| #if defined (WINDOWS_PHONE) |
| g_fFPS = (iActualFrameEncodedCount * 1.0f) / (float) dElapsed; |
| g_dEncoderTime = dElapsed; |
| g_iEncodedFrame = iActualFrameEncodedCount; |
| #endif |
| } |
| INSIDE_MEM_FREE: |
| for (int i = 0; i < sSvcParam.iSpatialLayerNum; ++i) { |
| if (pFpBs[i]) { |
| fclose (pFpBs[i]); |
| pFpBs[i] = NULL; |
| } |
| } |
| #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 |
| if (pFileYUV != NULL) { |
| fclose (pFileYUV); |
| pFileYUV = NULL; |
| } |
| if (pYUV) { |
| delete[] pYUV; |
| pYUV = NULL; |
| } |
| if (pSrcPic) { |
| delete pSrcPic; |
| pSrcPic = 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 ; |
| } |
| |
| int32_t CreateSVCEncHandle (ISVCEncoder** ppEncoder) { |
| int32_t ret = 0; |
| ret = WelsCreateSVCEncoder (ppEncoder); |
| return ret; |
| } |
| |
| void DestroySVCEncHandle (ISVCEncoder* pEncoder) { |
| if (pEncoder) { |
| WelsDestroySVCEncoder (pEncoder); |
| |
| } |
| } |
| |
| /**************************************************************************** |
| * main: |
| ****************************************************************************/ |
| #if defined(ANDROID_NDK) || defined(APPLE_IOS) || defined (WINDOWS_PHONE) |
| extern "C" int EncMain (int argc, char** argv) |
| #else |
| int main (int argc, char** argv) |
| #endif |
| { |
| ISVCEncoder* pSVCEncoder = 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 << "WelsCreateSVCEncoder() failed!!" << endl; |
| goto exit; |
| } |
| |
| if (argc < 2) { |
| goto exit; |
| } else { |
| if (!strstr (argv[1], ".cfg")) { // check configuration type (like .cfg?) |
| if (argc > 2) { |
| iRet = ProcessEncoding (pSVCEncoder, argc, argv, false); |
| 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 = ProcessEncoding (pSVCEncoder, argc, argv, true); |
| if (iRet > 0) |
| goto exit; |
| } |
| } |
| |
| DestroySVCEncHandle (pSVCEncoder); |
| return 0; |
| |
| exit: |
| DestroySVCEncHandle (pSVCEncoder); |
| PrintHelp(); |
| return 1; |
| } |