[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(¶m, X265_LOG_ERROR, "Unsupported color space (%d) on input",
+ pic_in->colorSpace);
+ return -1;
+ }
+ if (pic_in->bitDepth < param.internalBitDepth)
+ {
+ x265_log(¶m, 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", ¶m->sourceWidth, ¶m->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", ¶m->sourceWidth, ¶m->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