[x265] [PATCH RFC2] decouple input depth from internal depth; allow input depth != internal depth

Steve Borho steve at borho.org
Fri Feb 14 23:40:49 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 5fb18f01173f09371ba5b27c19e6257c8849d61a
# Parent  d77a549b8061f038b2d263c4508e256b4a335208
decouple input depth from internal depth; allow input depth != internal depth

Shift 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. No CLI option was added
for internal bit depth since it is not currently runtime configurable.

diff -r d77a549b8061 -r 5fb18f01173f source/Lib/TLibCommon/TComPicYuv.cpp
--- a/source/Lib/TLibCommon/TComPicYuv.cpp	Fri Feb 14 15:51:09 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,24 @@
      * warnings from valgrind about using uninitialized pixels */
     padx++; pady++;
 
-#if HIGH_BIT_DEPTH
-    if (pic.bitDepth > 8)
+    if (pic.bitDepth < X265_DEPTH)
     {
-        uint16_t *y = (uint16_t*)pic.planes[0];
-        uint16_t *u = (uint16_t*)pic.planes[1];
-        uint16_t *v = (uint16_t*)pic.planes[2];
+        /* 8bit input, 10bit internal depth. Do a simple up-shift of 2 bits */
+        assert(g_bitDepth == 10);
 
-        /* defensive programming, mask off bits that are supposed to be zero */
-        uint16_t mask = (1 << X265_DEPTH) - 1;
+        pixel *Y = getLumaAddr();
+        pixel *U = getCbAddr();
+        pixel *V = getCrAddr();
 
-        // Manually copy pixels to up-size them
+        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++)
         {
             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]) << 2;
             }
 
             Y += getStride();
@@ -213,14 +205,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]) << 2;
+                V[c] = ((pixel)v[c]) << 2;
             }
 
             U += getCStride();
@@ -228,43 +214,22 @@
             u += pic.stride[1];
             v += pic.stride[2];
         }
+    }
+    else if (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();
-
-            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
-    {
         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] = (Pel)y[c];
-            }
-
-            for (int x = 0; x < padx; x++)
-            {
-                Y[width + x] = Y[width - 1];
+                Y[c] = (pixel)y[c];
             }
 
             Y += getStride();
@@ -275,10 +240,76 @@
         {
             for (int c = 0; c < width >> m_hChromaShift; c++)
             {
-                U[c] = (Pel)u[c];
-                V[c] = (Pel)v[c];
+                U[c] = (pixel)u[c];
+                V[c] = (pixel)v[c];
             }
 
+            U += getCStride();
+            V += getCStride();
+            u += pic.stride[1];
+            v += pic.stride[2];
+        }
+    }
+    else /* pic.bitDepth > 8 */
+    {
+        pixel *Y = getLumaAddr();
+        pixel *U = getCbAddr();
+        pixel *V = getCrAddr();
+
+        uint16_t *y = (uint16_t*)pic.planes[0];
+        uint16_t *u = (uint16_t*)pic.planes[1];
+        uint16_t *v = (uint16_t*)pic.planes[2];
+
+        /* 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);
+
+        /* shift and mask pixels to final size */
+        for (int r = 0; r < height; r++)
+        {
+            for (int c = 0; c < width; c++)
+            {
+                Y[c] = (pixel)((y[c] >> shift) & mask);
+            }
+
+            Y += getStride();
+            y += pic.stride[0];
+        }
+
+        for (int r = 0; r < height >> m_vChromaShift; r++)
+        {
+            for (int c = 0; c < width >> m_hChromaShift; 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 +318,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 +339,4 @@
             memcpy(V + j * getCStride(), V, ((width + padx) >> m_hChromaShift) * sizeof(pixel));
         }
     }
-#endif // if HIGH_BIT_DEPTH
 }
diff -r d77a549b8061 -r 5fb18f01173f source/common/common.cpp
--- a/source/common/common.cpp	Fri Feb 14 15:51:09 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 d77a549b8061 -r 5fb18f01173f source/encoder/encoder.cpp
--- a/source/encoder/encoder.cpp	Fri Feb 14 15:51:09 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\n",
+                     pic_in->colorSpace);
+            return -1;
+        }
+        if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)
+        {
+            x265_log(&param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n",
+                     pic_in->bitDepth);
+            return -1;
+        }
+
         TComPic *pic;
         if (m_freeList.empty())
         {
@@ -247,7 +260,7 @@
             if (!pic || !pic->create(this))
             {
                 m_aborted = true;
-                x265_log(&param, X265_LOG_ERROR, "memory allocation failure, aborting encode");
+                x265_log(&param, X265_LOG_ERROR, "memory allocation failure, aborting encode\n");
                 if (pic)
                 {
                     pic->destroy(param.bframes);
@@ -858,7 +871,7 @@
                 p += sprintf(buf + p, "]");
             }
         }
-        x265_log(&param, X265_LOG_DEBUG, buf);
+        x265_log(&param, X265_LOG_DEBUG, "%s\n", buf);
 
         // per frame CSV logging if the file handle is valid
         if (m_csvfpt)
@@ -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 d77a549b8061 -r 5fb18f01173f source/x265.cpp
--- a/source/x265.cpp	Fri Feb 14 15:51:09 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,34 @@
     }
     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 < 8 || inputBitDepth > 16)
     {
-        this->input->setDimensions(param->sourceWidth, param->sourceHeight);
-        this->input->setBitDepth(param->inputBitDepth);
+        x265_log(param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n", inputBitDepth);
+        return true;
     }
 
     int guess = this->input->guessFrameCount();
@@ -529,11 +536,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 +549,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 +560,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 d77a549b8061 -r 5fb18f01173f source/x265.h
--- a/source/x265.h	Fri Feb 14 15:51:09 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