[x265] [PATCH RFC] decouple input depth from internal depth; allow input depth > internal depth

Steve Borho steve at borho.org
Fri Feb 14 22:51:23 CET 2014


# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1392414650 21600
#      Fri Feb 14 15:50:50 2014 -0600
# Node ID 93daea8a30981c53552e150f1234b0409af8933c
# Parent  da7f6bebdd30d7af46dd7f9bf3d4654a984abaeb
decouple input depth from internal depth; allow input depth > internal depth

Downshift and mask input samples as necessary to reach internal depth.  The
input depth is no longer in x265_param, where it never really belonged since
pic.bitDepth was the only number that mattered.

Add validations for input color space and bit depths.  For 16bpp builds, max
bit depth is 10, today, fixed x265_max_bit_depth.

No CLI option was added for internal bit depth since it is not currently runtime
configurable.

diff -r da7f6bebdd30 -r 93daea8a3098 source/Lib/TLibCommon/TComPicYuv.cpp
--- a/source/Lib/TLibCommon/TComPicYuv.cpp	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/Lib/TLibCommon/TComPicYuv.cpp	Fri Feb 14 15:50:50 2014 -0600
@@ -146,15 +146,10 @@
     return height;
 }
 
-/* Copy pixels from an input picture (C structure) into internal TComPicYuv instance
- * Upscale pixels from 8bits to 16 bits when required, but do not modify
- * pixels. */
+/* Copy pixels from an x265_picture into internal TComPicYuv instance.
+ * Downshift pixels as necessary, mask off bits above X265_DEPTH for safety. */
 void TComPicYuv::copyFromPicture(const x265_picture& pic, int32_t *pad)
 {
-    Pel *Y = getLumaAddr();
-    Pel *U = getCbAddr();
-    Pel *V = getCrAddr();
-
     /* m_picWidth is the width that is being encoded, padx indicates how many
      * of those pixels are padding to reach multiple of MinCU(4) size.
      *
@@ -182,27 +177,21 @@
      * warnings from valgrind about using uninitialized pixels */
     padx++; pady++;
 
-#if HIGH_BIT_DEPTH
-    if (pic.bitDepth > 8)
+    if (pic.bitDepth == 8)
     {
-        uint16_t *y = (uint16_t*)pic.planes[0];
-        uint16_t *u = (uint16_t*)pic.planes[1];
-        uint16_t *v = (uint16_t*)pic.planes[2];
+        pixel *Y = getLumaAddr();
+        pixel *U = getCbAddr();
+        pixel *V = getCrAddr();
 
-        /* defensive programming, mask off bits that are supposed to be zero */
-        uint16_t mask = (1 << X265_DEPTH) - 1;
+        uint8_t *y = (uint8_t*)pic.planes[0];
+        uint8_t *u = (uint8_t*)pic.planes[1];
+        uint8_t *v = (uint8_t*)pic.planes[2];
 
-        // Manually copy pixels to up-size them
         for (int r = 0; r < height; r++)
         {
             for (int c = 0; c < width; c++)
             {
-                Y[c] = (pixel)(y[c] & mask);
-            }
-
-            for (int x = 0; x < padx; x++)
-            {
-                Y[width + x] = Y[width - 1];
+                Y[c] = (pixel)y[c];
             }
 
             Y += getStride();
@@ -213,14 +202,8 @@
         {
             for (int c = 0; c < width >> m_hChromaShift; c++)
             {
-                U[c] = (pixel)(u[c] & mask);
-                V[c] = (pixel)(v[c] & mask);
-            }
-
-            for (int x = 0; x < padx >> m_hChromaShift; x++)
-            {
-                U[(width >> m_hChromaShift) + x] = U[(width >> m_hChromaShift) - 1];
-                V[(width >> m_hChromaShift) + x] = V[(width >> m_hChromaShift) - 1];
+                U[c] = (pixel)u[c];
+                V[c] = (pixel)v[c];
             }
 
             U += getCStride();
@@ -228,43 +211,27 @@
             u += pic.stride[1];
             v += pic.stride[2];
         }
+    }
+    else /* pic.bitDepth > 8 */
+    {
+        pixel *Y = getLumaAddr();
+        pixel *U = getCbAddr();
+        pixel *V = getCrAddr();
 
-        /* extend the bottom if height is not multiple of the minimum CU size */
-        if (pady)
-        {
-            Y = getLumaAddr() + (height - 1) * getStride();
-            U = getCbAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
-            V = getCrAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
+        uint16_t *y = (uint16_t*)pic.planes[0];
+        uint16_t *u = (uint16_t*)pic.planes[1];
+        uint16_t *v = (uint16_t*)pic.planes[2];
 
-            for (uint32_t i = 1; i <= pady; i++)
-            {
-                memcpy(Y + i * getStride(), Y, (width + padx) * sizeof(Pel));
-            }
+        /* defensive programming, mask off bits that are supposed to be zero */
+        uint16_t mask = (1 << X265_DEPTH) - 1;
+        int shift = X265_MAX(0, pic.bitDepth - X265_DEPTH);
 
-            for (uint32_t j = 1; j <= pady >> m_vChromaShift; j++)
-            {
-                memcpy(U + j * getCStride(), U, ((width + padx) >> m_hChromaShift) * sizeof(Pel));
-                memcpy(V + j * getCStride(), V, ((width + padx) >> m_hChromaShift) * sizeof(Pel));
-            }
-        }
-    }
-    else
-    {
-        uint8_t *y = (uint8_t*)pic.planes[0];
-        uint8_t *u = (uint8_t*)pic.planes[1];
-        uint8_t *v = (uint8_t*)pic.planes[2];
-
-        // Manually copy pixels to up-size them
+        /* shift and mask pixels to final size */
         for (int r = 0; r < height; r++)
         {
             for (int c = 0; c < width; c++)
             {
-                Y[c] = (Pel)y[c];
-            }
-
-            for (int x = 0; x < padx; x++)
-            {
-                Y[width + x] = Y[width - 1];
+                Y[c] = (pixel)((y[c] >> shift) & mask);
             }
 
             Y += getStride();
@@ -275,10 +242,36 @@
         {
             for (int c = 0; c < width >> m_hChromaShift; c++)
             {
-                U[c] = (Pel)u[c];
-                V[c] = (Pel)v[c];
+                U[c] = (pixel)((u[c] >> shift) & mask);
+                V[c] = (pixel)((v[c] >> shift) & mask);
             }
 
+            U += getCStride();
+            V += getCStride();
+            u += pic.stride[1];
+            v += pic.stride[2];
+        }
+    }
+
+    /* extend the right edge if width was not multiple of the minimum CU size */
+    if (padx)
+    {
+        pixel *Y = getLumaAddr();
+        pixel *U = getCbAddr();
+        pixel *V = getCrAddr();
+
+        for (int r = 0; r < height; r++)
+        {
+            for (int x = 0; x < padx; x++)
+            {
+                Y[width + x] = Y[width - 1];
+            }
+
+            Y += getStride();
+        }
+
+        for (int r = 0; r < height >> m_vChromaShift; r++)
+        {
             for (int x = 0; x < padx >> m_hChromaShift; x++)
             {
                 U[(width >> m_hChromaShift) + x] = U[(width >> m_hChromaShift) - 1];
@@ -287,70 +280,15 @@
 
             U += getCStride();
             V += getCStride();
-            u += pic.stride[1];
-            v += pic.stride[2];
-        }
-
-        /* extend the bottom if height is not multiple of the minimum CU size */
-        if (pady)
-        {
-            Y = getLumaAddr() + (height - 1) * getStride();
-            U = getCbAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
-            V = getCrAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
-
-            for (uint32_t i = 1; i <= pady; i++)
-            {
-                memcpy(Y + i * getStride(), Y, (width + padx) * sizeof(Pel));
-            }
-
-            for (uint32_t j = 1; j <= pady >> m_vChromaShift; j++)
-            {
-                memcpy(U + j * getCStride(), U, ((width + padx) >> m_hChromaShift) * sizeof(Pel));
-                memcpy(V + j * getCStride(), V, ((width + padx) >> m_hChromaShift) * sizeof(Pel));
-            }
         }
     }
-#else // if HIGH_BIT_DEPTH
-    uint8_t *y = (uint8_t*)pic.planes[0];
-    uint8_t *u = (uint8_t*)pic.planes[1];
-    uint8_t *v = (uint8_t*)pic.planes[2];
 
-    for (int r = 0; r < height; r++)
-    {
-        memcpy(Y, y, width);
-
-        /* extend the right if width is not multiple of the minimum CU size */
-        if (padx)
-            ::memset(Y + width, Y[width - 1], padx);
-
-        Y += getStride();
-        y += pic.stride[0];
-    }
-
-    for (int r = 0; r < height >> m_vChromaShift; r++)
-    {
-        memcpy(U, u, width >> m_hChromaShift);
-        memcpy(V, v, width >> m_hChromaShift);
-
-        /* extend the right if width is not multiple of the minimum CU size */
-        if (padx)
-        {
-            ::memset(U + (width >> m_hChromaShift), U[(width >> m_hChromaShift) - 1], padx >> m_hChromaShift);
-            ::memset(V + (width >> m_hChromaShift), V[(width >> m_hChromaShift) - 1], padx >> m_hChromaShift);
-        }
-
-        U += getCStride();
-        V += getCStride();
-        u += pic.stride[1];
-        v += pic.stride[2];
-    }
-
-    /* extend the bottom if height is not multiple of the minimum CU size */
+    /* extend the bottom if height was not multiple of the minimum CU size */
     if (pady)
     {
-        Y = getLumaAddr() + (height - 1) * getStride();
-        U = getCbAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
-        V = getCrAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
+        pixel *Y = getLumaAddr() + (height - 1) * getStride();
+        pixel *U = getCbAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
+        pixel *V = getCrAddr() + ((height >> m_vChromaShift) - 1) * getCStride();
 
         for (uint32_t i = 1; i <= pady; i++)
         {
@@ -363,5 +301,4 @@
             memcpy(V + j * getCStride(), V, ((width + padx) >> m_hChromaShift) * sizeof(pixel));
         }
     }
-#endif // if HIGH_BIT_DEPTH
 }
diff -r da7f6bebdd30 -r 93daea8a3098 source/common/common.cpp
--- a/source/common/common.cpp	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/common/common.cpp	Fri Feb 14 15:50:50 2014 -0600
@@ -149,7 +149,7 @@
     param->csvfn = NULL;
 
     /* Source specifications */
-    param->inputBitDepth = 8;
+    param->internalBitDepth = x265_max_bit_depth;
     param->internalCsp = X265_CSP_I420;
 
     /* CU definitions */
@@ -228,8 +228,7 @@
 {
     memset(pic, 0, sizeof(x265_picture));
 
-    /* This is the encoder internal bit depth */
-    pic->bitDepth = param->inputBitDepth;
+    pic->bitDepth = param->internalBitDepth;
     pic->colorSpace = param->internalCsp;
 }
 
@@ -243,9 +242,9 @@
     else if (!strcmp(profile, "main10"))
     {
 #if HIGH_BIT_DEPTH
-        param->inputBitDepth = 10;
+        param->internalBitDepth = 10;
 #else
-        x265_log(param, X265_LOG_WARNING, "not compiled for 16bpp. Falling back to main profile.\n");
+        x265_log(param, X265_LOG_WARNING, "Main10 not supported, not compiled for 16bpp.\n");
         return -1;
 #endif
     }
@@ -440,13 +439,13 @@
     CHECK(param->internalCsp != X265_CSP_I420,
           "Only 4:2:0 color space is supported at this time");
 #if HIGH_BIT_DEPTH
-    CHECK(param->inputBitDepth != 10,
+    CHECK(param->internalBitDepth != 10,
           "x265 was compiled for 10bit encodes, only 10bit inputs supported");
 #endif
 
-    CHECK(param->inputBitDepth > x265_max_bit_depth,
-          "inputBitDepth must be <= x265_max_bit_depth");
-    CHECK(param->rc.qp < -6 * (param->inputBitDepth - 8) || param->rc.qp > 51,
+    CHECK(param->internalBitDepth > x265_max_bit_depth,
+          "internalBitDepth must be <= x265_max_bit_depth");
+    CHECK(param->rc.qp < -6 * (param->internalBitDepth - 8) || param->rc.qp > 51,
           "QP exceeds supported range (-QpBDOffsety to 51)");
     CHECK(param->frameRate <= 0,
           "Frame rate must be more than 1");
@@ -537,9 +536,9 @@
             x265_log(param, X265_LOG_ERROR, "maxCUSize must be the same for all encoders in a single process");
             return -1;
         }
-        if (param->inputBitDepth != g_bitDepth)
+        if (param->internalBitDepth != g_bitDepth)
         {
-            x265_log(param, X265_LOG_ERROR, "inputBitDepth must be the same for all encoders in a single process");
+            x265_log(param, X265_LOG_ERROR, "internalBitDepth must be the same for all encoders in a single process");
             return -1;
         }
     }
@@ -548,7 +547,7 @@
         // set max CU width & height
         g_maxCUWidth  = param->maxCUSize;
         g_maxCUHeight = param->maxCUSize;
-        g_bitDepth = param->inputBitDepth;
+        g_bitDepth = param->internalBitDepth;
 
         // compute actual CU depth with respect to config depth and max transform size
         g_addCUDepth = 0;
@@ -577,7 +576,7 @@
     if (param->logLevel < X265_LOG_INFO)
         return;
 #if HIGH_BIT_DEPTH
-    x265_log(param, X265_LOG_INFO, "Input bit depth                     : %d\n", param->inputBitDepth);
+    x265_log(param, X265_LOG_INFO, "Internal bit depth                  : %d\n", param->internalBitDepth);
 #endif
     x265_log(param, X265_LOG_INFO, "CU size                             : %d\n", param->maxCUSize);
     x265_log(param, X265_LOG_INFO, "Max RQT depth inter / intra         : %d / %d\n", param->tuQTMaxInterDepth, param->tuQTMaxIntraDepth);
diff -r da7f6bebdd30 -r 93daea8a3098 source/common/version.cpp
--- a/source/common/version.cpp	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/common/version.cpp	Fri Feb 14 15:50:50 2014 -0600
@@ -75,7 +75,7 @@
 
 #if HIGH_BIT_DEPTH
 #define BITDEPTH "16bpp"
-const int x265_max_bit_depth = 12;
+const int x265_max_bit_depth = 10;
 #else
 #define BITDEPTH "8bpp"
 const int x265_max_bit_depth = 8;
diff -r da7f6bebdd30 -r 93daea8a3098 source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/encoder/encoder.cpp	Fri Feb 14 15:50:50 2014 -0600
@@ -240,6 +240,19 @@
 
     if (pic_in)
     {
+        if (pic_in->colorSpace != param.internalCsp)
+        {
+            x265_log(&param, X265_LOG_ERROR, "Unsupported color space (%d) on input",
+                     pic_in->colorSpace);
+            return -1;
+        }
+        if (pic_in->bitDepth < param.internalBitDepth)
+        {
+            x265_log(&param, X265_LOG_ERROR, "Input bit depth (%d) is less than internal depth (%d)",
+                     pic_in->bitDepth, param.internalBitDepth);
+            return -1;
+        }
+
         TComPic *pic;
         if (m_freeList.empty())
         {
@@ -1229,7 +1242,7 @@
         break;
     }
 
-    if (_param->inputBitDepth > 8)
+    if (_param->internalBitDepth > 8)
         m_profile = Profile::MAIN10;
     else if (_param->keyframeMax == 1)
         m_profile = Profile::MAINSTILLPICTURE;
diff -r da7f6bebdd30 -r 93daea8a3098 source/x265.cpp
--- a/source/x265.cpp	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/x265.cpp	Fri Feb 14 15:50:50 2014 -0600
@@ -282,7 +282,7 @@
     H0("-o/--output                      Bitstream output file name\n");
     H0("\nInput Options:\n");
     H0("   --input                       Raw YUV or Y4M input file name\n");
-    H0("   --input-depth                 Bit-depth of input file and internal encoder bit depth. Default %d\n", param->inputBitDepth);
+    H0("   --input-depth                 Bit-depth of input file. Default 8\n");
     H0("   --input-res                   Source picture size [w x h], auto-detected if Y4M\n");
     H0("   --input-csp                   Source color space parameter, auto-detected if Y4M\n");
     H0("   --fps                         Source frame rate, auto-detected if Y4M\n");
@@ -359,6 +359,7 @@
     int berror = 0;
     int help = 0;
     int cpuid = 0;
+    int inputBitDepth = 8;
     int reconFileBitDepth = 0;
     const char *inputfn = NULL;
     const char *reconfn = NULL;
@@ -442,7 +443,7 @@
             OPT("output") bitstreamfn = optarg;
             OPT("input") inputfn = optarg;
             OPT("recon") reconfn = optarg;
-            OPT("input-depth") param->inputBitDepth = (uint32_t)atoi(optarg);
+            OPT("input-depth") inputBitDepth = (uint32_t)atoi(optarg);
             OPT("recon-depth") reconFileBitDepth = (uint32_t)atoi(optarg);
             OPT("input-res") inputRes = optarg;
             OPT("no-scenecut") param->scenecutThreshold = 0; // special handling
@@ -480,7 +481,7 @@
         x265_log(param, X265_LOG_ERROR, "input or output file not specified, try -V for help\n");
         return true;
     }
-    this->input = Input::open(inputfn, param->inputBitDepth, bForceY4m);
+    this->input = Input::open(inputfn, inputBitDepth, bForceY4m);
     if (!this->input || this->input->isFail())
     {
         x265_log(param, X265_LOG_ERROR, "unable to open input file <%s>\n", inputfn);
@@ -488,28 +489,35 @@
     }
     if (this->input->getWidth())
     {
-        /* parse the width, height, frame rate from the y4m file */
+        /* the file knows its own dimensions, must be Y4M */
         param->internalCsp = this->input->getColorSpace();
         param->sourceWidth = this->input->getWidth();
         param->sourceHeight = this->input->getHeight();
         param->frameRate = (int)this->input->getRate();
     }
-    else if (inputRes)
+    else
     {
+        if (inputRes)
+            sscanf(inputRes, "%dx%d", &param->sourceWidth, &param->sourceHeight);
+        if (param->sourceHeight <= 0 || param->sourceWidth <= 0 || param->frameRate <= 0)
+        {
+            x265_log(param, X265_LOG_ERROR, "YUV input requires --input-res WxH and --fps N to be specified\n");
+            return true;
+        }
+        this->input->setDimensions(param->sourceWidth, param->sourceHeight);
         this->input->setColorSpace(param->internalCsp);
-        sscanf(inputRes, "%dx%d", &param->sourceWidth, &param->sourceHeight);
-        this->input->setDimensions(param->sourceWidth, param->sourceHeight);
-        this->input->setBitDepth(param->inputBitDepth);
+        this->input->setBitDepth(inputBitDepth);
     }
-    else if (param->sourceHeight <= 0 || param->sourceWidth <= 0 || param->frameRate <= 0)
+    if (param->internalCsp != X265_CSP_I420)
     {
-        x265_log(param, X265_LOG_ERROR, "YUV input requires source width, height, and rate to be specified\n");
+        x265_log(param, X265_LOG_ERROR, "Only I420 color space is supported in this build\n");
         return true;
     }
-    else
+    if (inputBitDepth < param->internalBitDepth)
     {
-        this->input->setDimensions(param->sourceWidth, param->sourceHeight);
-        this->input->setBitDepth(param->inputBitDepth);
+        x265_log(param, X265_LOG_ERROR, "Input bit depth (%d) may not be less than internal depth (%d)\n",
+                 inputBitDepth, param->internalBitDepth);
+        return true;
     }
 
     int guess = this->input->guessFrameCount();
@@ -529,11 +537,11 @@
         if (this->framesToBeEncoded == 0)
             fprintf(stderr, "%s  [info]: %dx%d %dHz %s, unknown frame count\n", input->getName(),
                     param->sourceWidth, param->sourceHeight, param->frameRate,
-                    (param->internalCsp >= X265_CSP_I444) ? "C444" : (param->internalCsp >= X265_CSP_I422) ? "C422" : "C420");
+                    x265_source_csp_names[param->internalCsp]);
         else
             fprintf(stderr, "%s  [info]: %dx%d %dHz %s, frames %u - %d of %d\n", input->getName(),
                     param->sourceWidth, param->sourceHeight, param->frameRate,
-                    (param->internalCsp >= X265_CSP_I444) ? "C444" : (param->internalCsp >= X265_CSP_I422) ? "C422" : "C420",
+                    x265_source_csp_names[param->internalCsp],
                     this->frameSkip, this->frameSkip + this->framesToBeEncoded - 1, fileFrameCount);
     }
 
@@ -542,7 +550,7 @@
     if (reconfn)
     {
         if (reconFileBitDepth == 0)
-            reconFileBitDepth = param->inputBitDepth;
+            reconFileBitDepth = param->internalBitDepth;
         this->recon = Output::open(reconfn, param->sourceWidth, param->sourceHeight, reconFileBitDepth, param->frameRate, param->internalCsp);
         if (this->recon->isFail())
         {
@@ -553,13 +561,13 @@
     }
 
 #if HIGH_BIT_DEPTH
-    if (param->inputBitDepth != 10)
+    if (param->internalBitDepth != 10)
     {
         x265_log(param, X265_LOG_ERROR, "Only bit depths of 10 are supported in this build\n");
         return true;
     }
 #else
-    if (param->inputBitDepth != 8)
+    if (param->internalBitDepth != 8)
     {
         x265_log(param, X265_LOG_ERROR, "Only bit depths of 8 are supported in this build\n");
         return true;
diff -r da7f6bebdd30 -r 93daea8a3098 source/x265.h
--- a/source/x265.h	Fri Feb 14 14:52:28 2014 -0600
+++ b/source/x265.h	Fri Feb 14 15:50:50 2014 -0600
@@ -333,12 +333,12 @@
      * types are MD5(1), CRC(2), Checksum(3).  Default is 0, none */
     int       decodedPictureHashSEI;
 
-    /*== Source Picture Specification ==*/
+    /*== Internal Picture Specification ==*/
 
     /* Internal encoder bit depth. If x265 was compiled to use 8bit pixels
      * (HIGH_BIT_DEPTH=0), this field must be 8, else this field must be 10.
      * Future builds may support 12bit pixels. */
-    int       inputBitDepth;
+    int       internalBitDepth;
 
     /* Color space of internal pictures. Only X265_CSP_I420 is currently supported.
      * Eventually, i422 and i444 will be supported as internal color spaces and other


More information about the x265-devel mailing list