[x265] [PATCH] level: move decoder level logic to level.cpp, make table based

Steve Borho steve at borho.org
Mon Apr 7 17:20:19 CEST 2014


# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1396883987 18000
#      Mon Apr 07 10:19:47 2014 -0500
# Node ID 5dde42e36a3d8a7bbe3565a61ae47927472e4ff6
# Parent  a4cb624267f317c999167ae9d5514241ea637c8f
level: move decoder level logic to level.cpp, make table based

the new function enforces more limits defined in Annex A -
  widths and heights are capped to avoid extreme rectangular dimensions
  DPB size is limited based on resolution
  at higher resolutions, the min CTU size is 32

this commit also lays the groundwork for adding --level 5.1 --tier HIGH params

diff -r a4cb624267f3 -r 5dde42e36a3d source/common/common.cpp
--- a/source/common/common.cpp	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/common/common.cpp	Mon Apr 07 10:19:47 2014 -0500
@@ -98,7 +98,7 @@
     return (x265_exp2_lut[i & 63] + 256) << (i >> 6) >> 8;
 }
 
-void x265_log(x265_param *param, int level, const char *fmt, ...)
+void x265_log(const x265_param *param, int level, const char *fmt, ...)
 {
     if (param && level > param->logLevel)
         return;
diff -r a4cb624267f3 -r 5dde42e36a3d source/common/common.h
--- a/source/common/common.h	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/common/common.h	Mon Apr 07 10:19:47 2014 -0500
@@ -145,7 +145,7 @@
 
 /* defined in common.cpp */
 int64_t x265_mdate(void);
-void x265_log(x265_param *param, int level, const char *fmt, ...);
+void x265_log(const x265_param *param, int level, const char *fmt, ...);
 int x265_exp2fix8(double x);
 void *x265_malloc(size_t size);
 void x265_free(void *ptr);
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/CMakeLists.txt
--- a/source/encoder/CMakeLists.txt	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/encoder/CMakeLists.txt	Mon Apr 07 10:19:47 2014 -0500
@@ -52,6 +52,7 @@
     frameencoder.cpp frameencoder.h
     framefilter.cpp framefilter.h
     cturow.cpp cturow.h
+    level.cpp level.h
     dpb.cpp dpb.h
     ratecontrol.cpp ratecontrol.h
     compress.cpp
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/api.cpp
--- a/source/encoder/api.cpp	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/encoder/api.cpp	Mon Apr 07 10:19:47 2014 -0500
@@ -25,6 +25,7 @@
 #include "param.h"
 #include "encoder.h"
 #include "frameencoder.h"
+#include "level.h"
 
 using namespace x265;
 
@@ -49,8 +50,8 @@
     if (encoder)
     {
         // these may change params for auto-detect, etc
-        encoder->determineLevelAndProfile(param);
         encoder->configure(param);
+        determineLevel(*param, encoder->m_profile, encoder->m_level, encoder->m_levelTier);
 
         encoder->param = X265_MALLOC(x265_param, 1);
         if (!encoder->param)
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/encoder/encoder.cpp	Mon Apr 07 10:19:47 2014 -0500
@@ -1195,126 +1195,6 @@
     pps->setLoopFilterAcrossTilesEnabledFlag(m_loopFilterAcrossTilesEnabledFlag);
 }
 
-void Encoder::determineLevelAndProfile(x265_param *_param)
-{
-    // this is all based on the table at on Wikipedia at
-    // http://en.wikipedia.org/wiki/High_Efficiency_Video_Coding#Profiles
-
-    // TODO: there are minimum CTU sizes for higher levels, needs to be enforced
-
-    uint32_t lumaSamples = _param->sourceWidth * _param->sourceHeight;
-    uint32_t samplesPerSec = (uint32_t)(lumaSamples * ((double)_param->fpsNum / _param->fpsDenom));
-    uint32_t bitrate = _param->rc.bitrate;
-
-    m_level = Level::LEVEL1;
-    const char *level = "1";
-    if (samplesPerSec > 552960 || lumaSamples > 36864 || bitrate > 128)
-    {
-        m_level = Level::LEVEL2;
-        level = "2";
-    }
-    if (samplesPerSec > 3686400 || lumaSamples > 122880 || bitrate > 1500)
-    {
-        m_level = Level::LEVEL2_1;
-        level = "2.1";
-    }
-    if (samplesPerSec > 7372800 || lumaSamples > 245760 || bitrate > 3000)
-    {
-        m_level = Level::LEVEL3;
-        level = "3";
-    }
-    if (samplesPerSec > 16588800 || lumaSamples > 552960 || bitrate > 6000)
-    {
-        m_level = Level::LEVEL3_1;
-        level = "3.1";
-    }
-    if (samplesPerSec > 33177600 || lumaSamples > 983040 || bitrate > 10000)
-    {
-        m_level = Level::LEVEL4;
-        level = "4";
-    }
-    if (samplesPerSec > 66846720 || bitrate > 30000)
-    {
-        m_level = Level::LEVEL4_1;
-        level = "4.1";
-    }
-    if (samplesPerSec > 133693440 || lumaSamples > 2228224 || bitrate > 50000)
-    {
-        m_level = Level::LEVEL5;
-        level = "5";
-    }
-    if (samplesPerSec > 267386880 || bitrate > 100000)
-    {
-        m_level = Level::LEVEL5_1;
-        level = "5.1";
-    }
-    if (samplesPerSec > 534773760 || bitrate > 160000)
-    {
-        m_level = Level::LEVEL5_2;
-        level = "5.2";
-    }
-    if (samplesPerSec > 1069547520 || lumaSamples > 8912896 || bitrate > 240000)
-    {
-        m_level = Level::LEVEL6;
-        level = "6";
-    }
-    if (samplesPerSec > 1069547520 || bitrate > 240000)
-    {
-        m_level = Level::LEVEL6_1;
-        level = "6.1";
-    }
-    if (samplesPerSec > 2139095040 || bitrate > 480000)
-    {
-        m_level = Level::LEVEL6_2;
-        level = "6.2";
-    }
-    if (samplesPerSec > 4278190080U || lumaSamples > 35651584 || bitrate > 800000)
-        x265_log(_param, X265_LOG_WARNING, "video size or bitrate out of scope for HEVC\n");
-
-    /* Within a given level, we might be at a high tier, depending on bitrate */
-    m_levelTier = Level::MAIN;
-    switch (m_level)
-    {
-    case Level::LEVEL4:
-        if (bitrate > 12000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL4_1:
-        if (bitrate > 20000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL5:
-        if (bitrate > 25000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL5_1:
-        if (bitrate > 40000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL5_2:
-        if (bitrate > 60000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL6:
-        if (bitrate > 60000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL6_1:
-        if (bitrate > 120000) m_levelTier = Level::HIGH;
-        break;
-    case Level::LEVEL6_2:
-        if (bitrate > 240000) m_levelTier = Level::HIGH;
-        break;
-    default:
-        break;
-    }
-
-    if (_param->internalBitDepth > 8)
-        m_profile = Profile::MAIN10;
-    else if (_param->keyframeMax == 1)
-        m_profile = Profile::MAINSTILLPICTURE;
-    else
-        m_profile = Profile::MAIN;
-
-    static const char *profiles[] = { "None", "Main", "Main10", "Mainstillpicture" };
-    static const char *tiers[]    = { "Main", "High" };
-    x265_log(_param, X265_LOG_INFO, "%s profile, Level-%s (%s tier)\n", profiles[m_profile], level, tiers[m_levelTier]);
-}
-
 void Encoder::configure(x265_param *p)
 {
     // Trim the thread pool if WPP is disabled
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/encoder.h
--- a/source/encoder/encoder.h	Sun Apr 06 17:31:49 2014 -0500
+++ b/source/encoder/encoder.h	Mon Apr 07 10:19:47 2014 -0500
@@ -224,8 +224,6 @@
 
     void configure(x265_param *param);
 
-    void determineLevelAndProfile(x265_param *param);
-
     int  extractNalData(NALUnitEBSP **nalunits, int& memsize);
 
     void updateVbvPlan(RateControl* rc);
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/level.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/encoder/level.cpp	Mon Apr 07 10:19:47 2014 -0500
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * Copyright (C) 2013 x265 project
+ *
+ * Authors: Steve Borho <steve at borho.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
+ *
+ * This program is also available under a commercial proprietary license.
+ * For more information, contact us at licensing at multicorewareinc.com.
+ *****************************************************************************/
+
+#include "common.h"
+#include "TLibCommon/CommonDef.h"
+#include "level.h"
+
+namespace x265 {
+
+typedef struct
+{
+    uint32_t maxLumaSamples;
+    uint32_t maxLumaSamplesPerSecond;
+    uint32_t maxBitrateMain;
+    uint32_t maxBitrateHigh;
+    uint32_t minCompressionRatio;
+    Level::Name levelEnum;
+    const char* name;
+} LevelSpec;
+
+LevelSpec levels[] = {
+    { MAX_UINT, MAX_UINT,   MAX_UINT, MAX_UINT, 0, Level::NONE, "none" },
+    { 36864,    552960,     128,      MAX_UINT, 2, Level::LEVEL1, "1" },
+    { 122880,   3686400,    1500,     MAX_UINT, 2, Level::LEVEL2, "2" },
+    { 245760,   7372800,    3000,     MAX_UINT, 2, Level::LEVEL2_1, "2.1" },
+    { 552960,   16588800,   6000,     MAX_UINT, 2, Level::LEVEL3, "3" },
+    { 983040,   33177600,   10000,    MAX_UINT, 2, Level::LEVEL3_1, "3.1" },
+    { 983040,   66846720,   12000,    30000,    4, Level::LEVEL4, "4" },
+    { 2228224,  133693440,  20000,    50000,    4, Level::LEVEL4_1, "4.1" },
+    { 2228224,  267386880,  25000,    100000,   6, Level::LEVEL5, "5" },
+    { 2228224,  534773760,  40000,    160000,   8, Level::LEVEL5_1, "5.1" },
+    { 8912896,  1069547520, 60000,    240000,   8, Level::LEVEL5_2, "5.2" },
+    { 8912896,  1069547520, 60000,    240000,   8, Level::LEVEL6, "6" },
+    { 8912896,  2139095040, 120000,   480000,   8, Level::LEVEL6_1, "6.1" },
+    { 35651584, 4278190080, 240000,   800000,   6, Level::LEVEL6_2, "6.2" },
+    { 0, 0, 0, 0, 0, Level::NONE, "\0"}
+};
+
+/* determine minimum decoder level requiremented to decode the described video */
+void determineLevel(const x265_param &param, Profile::Name& profile, Level::Name& level, Level::Tier& tier)
+{
+    profile = Profile::NONE;
+    if (param.internalCsp == X265_CSP_I420)
+    {
+        /* other misc requirements that we enforce in other areas:
+         * 1. chroma bitdpeth is same as luma bit depth
+         * 2. CTU size is 16, 32, or 64
+         * 3. some limitations on tiles, we do not support tiles
+         *
+         * Technically, Mainstillpicture implies one picture per bitstream but
+         * we do not enforce this limit. We do repeat SPS, PPS, and VPS each
+         * frame */
+
+        if (param.internalBitDepth == 8 && param.keyframeMax == 1)
+            profile = Profile::MAINSTILLPICTURE;
+        if (param.internalBitDepth == 8)
+            profile = Profile::MAIN;
+        else if (param.internalBitDepth == 10)
+            profile = Profile::MAIN10;
+
+    }
+    /* TODO: Range extension profiles */
+
+    uint32_t lumaSamples = param.sourceWidth * param.sourceHeight;
+    uint32_t samplesPerSec = (uint32_t)(lumaSamples * ((double)param.fpsNum / param.fpsDenom));
+    uint32_t bitrate = param.rc.bitrate; /* TODO: vbv-maxrate? */
+
+    /* TODO; Keep in sync with encoder.cpp, or pass in maxDecPicBuffering */
+    int numReorderPics = (param.bBPyramid && param.bframes > 1) ? 2 : 1;
+    int maxDecPicBuffering = X265_MIN(MAX_NUM_REF, X265_MAX(numReorderPics + 1, param.maxNumReferences) + numReorderPics);
+    const int MaxDpbPicBuf = 6;
+
+    level = Level::NONE;
+    tier = Level::MAIN;
+    const char *levelName = "(none)";
+
+    for (int i = 1; levels[i].maxLumaSamples; i++)
+    {
+        if (lumaSamples > levels[i].maxLumaSamples)
+            continue;
+        else if (samplesPerSec > levels[i].maxLumaSamplesPerSecond)
+            continue;
+        else if (bitrate > levels[i].maxBitrateHigh)
+            continue;
+        else if (param.sourceWidth > sqrt(levels[i].maxLumaSamples * 8))
+            continue;
+        else if (param.sourceHeight > sqrt(levels[i].maxLumaSamples * 8))
+            continue;
+
+        int maxDpbSize = MaxDpbPicBuf;
+        if (lumaSamples <= (levels[i].maxLumaSamples >> 2))
+            maxDpbSize = X265_MIN(4 * MaxDpbPicBuf, 16);
+        else if (lumaSamples <= (levels[i].maxLumaSamples >> 1))
+            maxDpbSize = X265_MIN(2 * MaxDpbPicBuf, 16);
+        else if (lumaSamples <= ((3 * levels[i].maxLumaSamples) >> 2))
+            maxDpbSize = X265_MIN((4 * MaxDpbPicBuf) / 3, 16);
+        /* The value of sps_max_dec_pic_buffering_minus1[ HighestTid ] + 1 shall be less than
+         * or equal to MaxDpbSize */
+        if (maxDecPicBuffering > maxDpbSize)
+            continue;
+
+        /* For level 5 and higher levels, the value of CtbSizeY shall be equal to 32 or 64 */
+        if (levels[i].levelEnum >= Level::LEVEL5 && param.maxCUSize < 32)
+            continue;
+
+        level = levels[i].levelEnum;
+        levelName = levels[i].name;
+        if (bitrate > levels[i].maxBitrateMain && bitrate <= levels[i].maxBitrateHigh &&
+            levels[i].maxBitrateHigh != MAX_UINT)
+            tier = Level::HIGH;
+        else
+            tier = Level::MAIN;
+        /* TODO: The value of NumPocTotalCurr shall be less than or equal to 8 */
+        break;
+    }
+
+    static const char *profiles[] = { "None", "Main", "Main10", "Mainstillpicture" };
+    static const char *tiers[]    = { "Main", "High" };
+    x265_log(&param, X265_LOG_INFO, "%s profile, Level-%s (%s tier)\n", profiles[profile], levelName, tiers[tier]);
+}
+
+/* enforce a maximum decoder level requirement, in other words assure that a
+ * decoder of the specified level may decode the video about to be created.
+ * Lower parameters where necessary to ensure the video will be decodable by a
+ * decoder meeting this level of requirement.  Some parameters (resolution and
+ * frame rate) are non-negotiable and thus this function may fail. In those
+ * circumstances it will be quite noisy */
+void enforceLevel(x265_param& param, int level, bool bHighTier)
+{
+    uint32_t lumaSamples = param.sourceWidth * param.sourceHeight;
+    uint32_t samplesPerSec = (uint32_t)(lumaSamples * ((double)param.fpsNum / param.fpsDenom));
+    LevelSpec& l = levels[level];
+
+    bool ok = true;
+    if (lumaSamples > l.maxLumaSamples)
+        ok = false;
+    else if (param.sourceWidth > sqrt(l.maxLumaSamples * 8))
+        ok = false;
+    else if (param.sourceHeight > sqrt(l.maxLumaSamples * 8))
+        ok = false;
+    if (!ok)
+        x265_log(&param, X265_LOG_WARNING, "picture dimensions are out of range for specified level\n");
+    else if (samplesPerSec > l.maxLumaSamplesPerSecond)
+        x265_log(&param, X265_LOG_WARNING, "frame rate is out of range for specified level\n");
+
+    if (bHighTier && param.rc.bitrate > (int)l.maxBitrateHigh && l.maxBitrateHigh != MAX_UINT)
+    {
+        param.rc.bitrate = l.maxBitrateHigh;
+        x265_log(&param, X265_LOG_INFO, "Lowering target bitrate to High tier limit of %dKbps\n", param.rc.bitrate);
+    }
+    else if (!bHighTier && param.rc.bitrate > (int)l.maxBitrateMain)
+    {
+        param.rc.bitrate = l.maxBitrateMain;
+        x265_log(&param, X265_LOG_INFO, "Lowering target bitrate to Main tier limit of %dKbps\n", param.rc.bitrate);
+    }
+
+    const int MaxDpbPicBuf = 6;
+    int maxDpbSize = MaxDpbPicBuf;
+    if (lumaSamples <= (l.maxLumaSamples >> 2))
+        maxDpbSize = X265_MIN(4 * MaxDpbPicBuf, 16);
+    else if (lumaSamples <= (l.maxLumaSamples >> 1))
+        maxDpbSize = X265_MIN(2 * MaxDpbPicBuf, 16);
+    else if (lumaSamples <= ((3 * l.maxLumaSamples) >> 2))
+        maxDpbSize = X265_MIN((4 * MaxDpbPicBuf) / 3, 16);
+    int savedRefCount = param.maxNumReferences;
+
+    do {
+        int numReorderPics = (param.bBPyramid && param.bframes > 1) ? 2 : 1;
+        int maxDecPicBuffering = X265_MIN(MAX_NUM_REF, X265_MAX(numReorderPics + 1, param.maxNumReferences) + numReorderPics);
+
+        if (maxDecPicBuffering > maxDpbSize)
+            param.maxNumReferences--;
+        else
+            break;
+    }
+    while (1);
+
+    if (param.maxNumReferences != savedRefCount)
+    {
+        x265_log(&param, X265_LOG_INFO, "Lowering max references to %d\n", param.maxNumReferences);
+    }
+}
+}
diff -r a4cb624267f3 -r 5dde42e36a3d source/encoder/level.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/encoder/level.h	Mon Apr 07 10:19:47 2014 -0500
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * Copyright (C) 2013 x265 project
+ *
+ * Authors: Steve Borho <steve at borho.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
+ *
+ * This program is also available under a commercial proprietary license.
+ * For more information, contact us at licensing at multicorewareinc.com.
+ *****************************************************************************/
+
+#ifndef X265_LEVEL_H
+#define X265_LEVEL_H 1
+
+#include "common.h"
+#include "TLibCommon/CommonDef.h"
+#include "x265.h"
+
+namespace x265 {
+
+void determineLevel(const x265_param &param, Profile::Name& profile, Level::Name& level, Level::Tier& tier);
+void enforceLevel(x265_param& param, int level, bool bHighTier);
+
+}
+
+#endif


More information about the x265-devel mailing list