diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2014-12-02 15:13:52 +0100 |
---|---|---|
committer | Hans Verkuil <hans.verkuil@cisco.com> | 2014-12-02 15:18:51 +0100 |
commit | c470c46413330134aea089e8121744ad8fa792d7 (patch) | |
tree | 2d6788f162c2008bb31a99cfb16c0d3eeadbc3e7 | |
parent | 5e259d546ce08fec67aef2de2c8576920277ca41 (diff) |
qv4l2: add support for ycbcr_enc and quantization
Support the new colorspace functionality.
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r-- | utils/qv4l2/capture-win-gl.cpp | 226 | ||||
-rw-r--r-- | utils/qv4l2/capture-win-gl.h | 9 | ||||
-rw-r--r-- | utils/qv4l2/capture-win-qt.h | 2 | ||||
-rw-r--r-- | utils/qv4l2/capture-win.h | 2 | ||||
-rw-r--r-- | utils/qv4l2/general-tab.cpp | 93 | ||||
-rw-r--r-- | utils/qv4l2/general-tab.h | 12 | ||||
-rw-r--r-- | utils/qv4l2/qv4l2.cpp | 48 | ||||
-rw-r--r-- | utils/qv4l2/qv4l2.h | 4 | ||||
-rw-r--r-- | utils/qv4l2/tpg-tab.cpp | 94 |
9 files changed, 407 insertions, 83 deletions
diff --git a/utils/qv4l2/capture-win-gl.cpp b/utils/qv4l2/capture-win-gl.cpp index b727ba79..f2298666 100644 --- a/utils/qv4l2/capture-win-gl.cpp +++ b/utils/qv4l2/capture-win-gl.cpp @@ -91,10 +91,10 @@ bool CaptureWinGL::isSupported() #endif } -void CaptureWinGL::setColorspace(unsigned colorspace) +void CaptureWinGL::setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv) { #ifdef HAVE_QTGL - m_videoSurface.setColorspace(colorspace); + m_videoSurface.setColorspace(colorspace, ycbcr_enc, quantization, is_sdtv); #endif } @@ -133,6 +133,9 @@ CaptureWinGLEngine::CaptureWinGLEngine() : m_WCrop(0), m_HCrop(0), m_colorspace(V4L2_COLORSPACE_REC709), + m_ycbcr_enc(V4L2_YCBCR_ENC_DEFAULT), + m_quantization(V4L2_QUANTIZATION_DEFAULT), + m_is_sdtv(false), m_field(V4L2_FIELD_NONE), m_displayColorspace(V4L2_COLORSPACE_SRGB), m_screenTextureCount(0), @@ -151,7 +154,7 @@ CaptureWinGLEngine::~CaptureWinGLEngine() clearShader(); } -void CaptureWinGLEngine::setColorspace(unsigned colorspace) +void CaptureWinGLEngine::setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv) { switch (colorspace) { case V4L2_COLORSPACE_SMPTE170M: @@ -160,6 +163,8 @@ void CaptureWinGLEngine::setColorspace(unsigned colorspace) case V4L2_COLORSPACE_470_SYSTEM_M: case V4L2_COLORSPACE_470_SYSTEM_BG: case V4L2_COLORSPACE_SRGB: + case V4L2_COLORSPACE_ADOBERGB: + case V4L2_COLORSPACE_BT2020: break; default: // If the colorspace was not specified, then guess @@ -174,7 +179,7 @@ void CaptureWinGLEngine::setColorspace(unsigned colorspace) case V4L2_PIX_FMT_NV16M: case V4L2_PIX_FMT_NV61M: // SDTV or HDTV? - if (m_frameWidth <= 720 && m_frameHeight <= 576) + if (is_sdtv) colorspace = V4L2_COLORSPACE_SMPTE170M; else colorspace = V4L2_COLORSPACE_REC709; @@ -185,9 +190,13 @@ void CaptureWinGLEngine::setColorspace(unsigned colorspace) } break; } - if (m_colorspace == colorspace) + if (m_colorspace == colorspace && m_ycbcr_enc == ycbcr_enc && + m_quantization == quantization && m_is_sdtv == is_sdtv) return; m_colorspace = colorspace; + m_ycbcr_enc = ycbcr_enc; + m_quantization = quantization; + m_is_sdtv = is_sdtv; m_formatChange = true; } @@ -487,6 +496,45 @@ void CaptureWinGLEngine::configureTexture(size_t idx) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } +// Normalize y to [0...1] and uv to [-0.5...0.5], taking into account the +// colorspace. +QString CaptureWinGLEngine::codeYUVNormalize() +{ + switch (m_colorspace) { + case V4L2_COLORSPACE_SRGB: + if (m_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT || + m_ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) + // sYCC is always full range + return ""; + /* fall through */ + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_SMPTE240M: + case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_470_SYSTEM_M: + case V4L2_COLORSPACE_470_SYSTEM_BG: + case V4L2_COLORSPACE_ADOBERGB: + case V4L2_COLORSPACE_BT2020: + default: + if (m_ycbcr_enc == V4L2_YCBCR_ENC_XV601 || + m_ycbcr_enc == V4L2_YCBCR_ENC_XV709 || + m_quantization == V4L2_QUANTIZATION_FULL_RANGE) + return ""; + return QString(" y = (255.0 / 219.0) * (y - (16.0 / 255.0));" + " u = (255.0 / 224.0) * u;" + " v = (255.0 / 224.0) * v;" + ); + } +} + +// Normalize r, g and b to [0...1] +QString CaptureWinGLEngine::codeRGBNormalize() +{ + return QString(" r = (255.0 / 219.0) * (r - (16.0 / 255.0));" + " g = (255.0 / 219.0) * (g - (16.0 / 255.0));" + " b = (255.0 / 219.0) * (b - (16.0 / 255.0));" + ); +} + // Convert Y'CbCr (aka YUV) to R'G'B', taking into account the // colorspace. QString CaptureWinGLEngine::codeYUV2RGB() @@ -495,25 +543,53 @@ QString CaptureWinGLEngine::codeYUV2RGB() case V4L2_COLORSPACE_SMPTE240M: // Old obsolete HDTV standard. Replaced by REC 709. // SMPTE 240M has its own luma coefficients - return QString(" float r = y + 1.8007 * v;" - " float g = y - 0.2575 * u - 0.57143 * v;" - " float b = y + 2.088 * u;" + return QString(" float r = y + 1.5756 * v;" + " float g = y - 0.2253 * u - 0.4768 * v;" + " float b = y + 1.8270 * u;" + ); + case V4L2_COLORSPACE_BT2020: + if (m_ycbcr_enc != V4L2_YCBCR_ENC_BT2020_CONST_LUM) { + // BT.2020 luma coefficients + return QString(" float r = y + 1.4719 * v;" + " float g = y - 0.1646 * u - 0.5703 * v;" + " float b = y + 1.8814 * u;" + ); + } + // BT.2020_CONST_LUM luma coefficients + return QString(" float b = u <= 0.0 ? y + 1.9404 * u : y + 1.5816 * u;" + " float r = v <= 0.0 ? y + 1.7184 * v : y + 0.9936 * v;" + " float lin_r = (r < 0.081) ? r / 4.5 : pow((r + 0.099) / 1.099, 1.0 / 0.45);" + " float lin_b = (b < 0.081) ? b / 4.5 : pow((b + 0.099) / 1.099, 1.0 / 0.45);" + " float lin_y = (y < 0.081) ? y / 4.5 : pow((y + 0.099) / 1.099, 1.0 / 0.45);" + " float lin_g = lin_y / 0.6780 - lin_r * 0.2627 / 0.6780 - lin_b * 0.0593 / 0.6780;" + " float g = (lin_g < 0.018) ? lin_g * 4.5 : 1.099 * pow(lin_g, 0.45) - 0.099;" ); case V4L2_COLORSPACE_SMPTE170M: case V4L2_COLORSPACE_470_SYSTEM_M: case V4L2_COLORSPACE_470_SYSTEM_BG: - // These SDTV colorspaces all use the BT.601 luma coefficients - return QString(" float r = y + 1.5958 * v;" - " float g = y - 0.39173 * u - 0.81290 * v;" - " float b = y + 2.017 * u;" + case V4L2_COLORSPACE_SRGB: + case V4L2_COLORSPACE_ADOBERGB: + if (m_ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT || + m_ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM) + // These colorspaces all use the BT.601 luma coefficients + return QString(" float r = y + 1.403 * v;" + " float g = y - 0.344 * u - 0.714 * v;" + " float b = y + 1.773 * u;" ); + /* fall-through */ case V4L2_COLORSPACE_REC709: - case V4L2_COLORSPACE_SRGB: + if (m_ycbcr_enc == V4L2_YCBCR_ENC_601 || + m_ycbcr_enc == V4L2_YCBCR_ENC_XV601) + return QString(" float r = y + 1.403 * v;" + " float g = y - 0.344 * u - 0.714 * v;" + " float b = y + 1.773 * u;" + ); + /* fall-through */ default: - // The HDTV/graphics colorspaces all use REC 709 luma coefficients - return QString(" float r = y + 1.79274 * v;" - " float g = y - 0.21325 * u - 0.53291 * v;" - " float b = y + 2.1124 * u;" + // The HDTV colorspaces all use REC 709 luma coefficients + return QString(" float r = y + 1.5701 * v;" + " float g = y - 0.1870 * u - 0.4664 * v;" + " float b = y + 1.8556 * u;" ); } } @@ -532,16 +608,27 @@ QString CaptureWinGLEngine::codeTransformToLinear() ); case V4L2_COLORSPACE_SRGB: // This is used for sRGB as specified by the IEC FDIS 61966-2-1 standard - return QString(" r = (r <= 0.03928) ? r / 12.92 : pow((r + 0.055) / 1.055, 2.4);" - " g = (g <= 0.03928) ? g / 12.92 : pow((g + 0.055) / 1.055, 2.4);" - " b = (b <= 0.03928) ? b / 12.92 : pow((b + 0.055) / 1.055, 2.4);" + return QString(" r = (r < -0.04045) ? -pow((-r + 0.055) / 1.055, 2.4) : " + " ((r <= 0.04045) ? r / 12.92 : pow((r + 0.055) / 1.055, 2.4));" + " g = (g < -0.04045) ? -pow((-g + 0.055) / 1.055, 2.4) : " + " ((g <= 0.04045) ? g / 12.92 : pow((g + 0.055) / 1.055, 2.4));" + " b = (b < -0.04045) ? -pow((-b + 0.055) / 1.055, 2.4) : " + " ((b <= 0.04045) ? b / 12.92 : pow((b + 0.055) / 1.055, 2.4));" ); + case V4L2_COLORSPACE_ADOBERGB: + return QString(" r = pow(r, 2.19921875);" + " g = pow(g, 2.19921875);" + " b = pow(b, 2.19921875);"); case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_BT2020: default: // All others use the transfer function specified by REC 709 - return QString(" r = (r < 0.081) ? r / 4.5 : pow((r + 0.099) / 1.099, 1.0 / 0.45);" - " g = (g < 0.081) ? g / 4.5 : pow((g + 0.099) / 1.099, 1.0 / 0.45);" - " b = (b < 0.081) ? b / 4.5 : pow((b + 0.099) / 1.099, 1.0 / 0.45);" + return QString(" r = (r <= -0.081) ? -pow((r - 0.099) / -1.099, 1.0 / 0.45) : " + " ((r < 0.081) ? r / 4.5 : pow((r + 0.099) / 1.099, 1.0 / 0.45));" + " g = (g <= -0.081) ? -pow((g - 0.099) / -1.099, 1.0 / 0.45) : " + " ((g < 0.081) ? g / 4.5 : pow((g + 0.099) / 1.099, 1.0 / 0.45));" + " b = (b <= -0.081) ? -pow((b - 0.099) / -1.099, 1.0 / 0.45) : " + " ((b < 0.081) ? b / 4.5 : pow((b + 0.099) / 1.099, 1.0 / 0.45));" ); } } @@ -552,6 +639,7 @@ QString CaptureWinGLEngine::codeColorspaceConversion() { switch (m_colorspace) { case V4L2_COLORSPACE_SMPTE170M: + case V4L2_COLORSPACE_SMPTE240M: // Current SDTV standard, although slowly being replaced by REC 709. // Use the SMPTE 170M aka SMPTE-C aka SMPTE RP 145 conversion matrix. return QString(" float rr = 0.939536 * r + 0.050215 * g + 0.001789 * b;" @@ -559,14 +647,6 @@ QString CaptureWinGLEngine::codeColorspaceConversion() " float bb = -0.001591 * r - 0.004356 * g + 1.005951 * b;" " r = rr; g = gg; b = bb;" ); - case V4L2_COLORSPACE_SMPTE240M: - // Old obsolete HDTV standard. Replaced by REC 709. - // Use the SMPTE 240M conversion matrix. - return QString(" float rr = 1.4086 * r - 0.4086 * g;" - " float gg = -0.0257 * r + 1.0457 * g;" - " float bb = -0.0254 * r - 0.0440 * g + 1.0695 * b;" - " r = rr; g = gg; b = bb;" - ); case V4L2_COLORSPACE_470_SYSTEM_M: // Old obsolete NTSC standard. Replaced by REC 709. // Use the NTSC 1953 conversion matrix. @@ -582,6 +662,17 @@ QString CaptureWinGLEngine::codeColorspaceConversion() " float bb = -0.0119 * g + 1.0119 * b;" " r = rr; b = bb;" ); + case V4L2_COLORSPACE_ADOBERGB: + return QString(" float rr = 1.3982832 * r - 0.3982831 * g;" + " float bb = -0.0429383 * g + 1.0429383 * b;" + " r = rr; b = bb;" + ); + case V4L2_COLORSPACE_BT2020: + return QString(" float rr = 1.6603627 * r - 0.5875400 * g - 0.0728227 * b;" + " float gg = -0.1245635 * r + 1.1329114 * g - 0.0083478 * b;" + " float bb = -0.0181566 * r - 0.1006017 * g + 1.1187583 * b;" + " r = rr; g = gg; b = bb;" + ); case V4L2_COLORSPACE_REC709: case V4L2_COLORSPACE_SRGB: default: @@ -608,16 +699,28 @@ QString CaptureWinGLEngine::codeTransformToNonLinear() // is available. if (m_haveFramebufferSRGB) return ""; - return QString(" r = (r <= 0.0031308) ? r * 12.92 : 1.055 * pow(r, 1.0 / 2.4) - 0.055;" - " g = (g <= 0.0031308) ? g * 12.92 : 1.055 * pow(g, 1.0 / 2.4) - 0.055;" - " b = (b <= 0.0031308) ? b * 12.92 : 1.055 * pow(b, 1.0 / 2.4) - 0.055;" + return QString(" r = (r < -0.0031308) ? -1.055 * pow(-r, 1.0 / 2.4) + 0.055 : " + " ((r <= 0.0031308) ? r * 12.92 : 1.055 * pow(r, 1.0 / 2.4) - 0.055);" + " g = (g < -0.0031308) ? -1.055 * pow(-g, 1.0 / 2.4) + 0.055 : " + " ((g <= 0.0031308) ? g * 12.92 : 1.055 * pow(g, 1.0 / 2.4) - 0.055);" + " b = (b < -0.0031308) ? -1.055 * pow(-b, 1.0 / 2.4) + 0.055 : " + " ((b <= 0.0031308) ? b * 12.92 : 1.055 * pow(b, 1.0 / 2.4) - 0.055);" + ); + case V4L2_COLORSPACE_ADOBERGB: + // Use the AdobeRGB transfer function + return QString(" r = pow(r, 1.0 / 2.19921875);" + " g = pow(g, 1.0 / 2.19921875);" + " b = pow(b, 1.0 / 2.19921875);" ); case V4L2_COLORSPACE_REC709: default: // Use the REC 709 transfer function - return QString(" r = (r < 0.018) ? r * 4.5 : 1.099 * pow(r, 0.45) - 0.099;" - " g = (g < 0.018) ? g * 4.5 : 1.099 * pow(g, 0.45) - 0.099;" - " b = (b < 0.018) ? b * 4.5 : 1.099 * pow(b, 0.45) - 0.099;" + return QString(" r = (r <= -0.018) ? -1.099 * pow(-r, 0.45) + 0.099 : " + " ((r < 0.018) ? r * 4.5 : 1.099 * pow(r, 0.45) - 0.099);" + " g = (g <= -0.018) ? -1.099 * pow(-g, 0.45) + 0.099 : " + " ((g < 0.018) ? g * 4.5 : 1.099 * pow(g, 0.45) - 0.099);" + " b = (b <= -0.018) ? -1.099 * pow(-b, 0.45) + 0.099 : " + " ((b < 0.018) ? b * 4.5 : 1.099 * pow(b, 0.45) - 0.099);" ); } } @@ -665,11 +768,12 @@ void CaptureWinGLEngine::shader_YUV() else if (m_field == V4L2_FIELD_SEQ_BT) codeHead += " xy.y = (mod(ycoord, 2.0) == 0.0) ? xy.y / 2.0 + 0.5 : xy.y / 2.0;"; - codeHead += " float y = 1.1640625 * (texture2D(ytex, xy).r - 0.0625);" - " float u = texture2D(utex, xy).r - 0.5;" - " float v = texture2D(vtex, xy).r - 0.5;"; + codeHead += " float y = texture2D(ytex, xy).r;" + " float u = texture2D(utex, xy).r - 0.5;" + " float v = texture2D(vtex, xy).r - 0.5;"; - QString codeTail = codeYUV2RGB() + + QString codeTail = codeYUVNormalize() + + codeYUV2RGB() + codeTransformToLinear() + codeColorspaceConversion() + codeTransformToNonLinear() + @@ -785,15 +889,14 @@ void CaptureWinGLEngine::shader_NV16M(__u32 format) else if (m_field == V4L2_FIELD_SEQ_BT) codeHead += " xy.y = (mod(ycoord, 2.0) == 0.0) ? xy.y / 2.0 + 0.5 : xy.y / 2.0;"; - codeHead += " float u, v;" - " float xcoord = floor(xy.x * tex_w);" - " float y = 1.1640625 * (texture2D(ytex, xy).r - 0.0625);"; - - + codeHead += " float u, v;" + " float xcoord = floor(xy.x * tex_w);" + " float y = texture2D(ytex, xy).r;"; QString codeBody = shader_NV16M_invariant(format); - QString codeTail = codeYUV2RGB() + + QString codeTail = codeYUVNormalize() + + codeYUV2RGB() + codeTransformToLinear() + codeColorspaceConversion() + codeTransformToNonLinear() + @@ -842,10 +945,10 @@ QString CaptureWinGLEngine::shader_YUY2_invariant(__u32 format) case V4L2_PIX_FMT_YUYV: return QString("if (mod(xcoord, 2.0) == 0.0) {" " luma_chroma = texture2D(tex, xy);" - " y = (luma_chroma.r - 0.0625) * 1.1643;" + " y = luma_chroma.r;" "} else {" " luma_chroma = texture2D(tex, vec2(xy.x - texl_w, xy.y));" - " y = (luma_chroma.b - 0.0625) * 1.1643;" + " y = luma_chroma.b;" "}" "u = luma_chroma.g - 0.5;" "v = luma_chroma.a - 0.5;" @@ -854,10 +957,10 @@ QString CaptureWinGLEngine::shader_YUY2_invariant(__u32 format) case V4L2_PIX_FMT_YVYU: return QString("if (mod(xcoord, 2.0) == 0.0) {" " luma_chroma = texture2D(tex, xy);" - " y = (luma_chroma.r - 0.0625) * 1.1643;" + " y = luma_chroma.r;" "} else {" " luma_chroma = texture2D(tex, vec2(xy.x - texl_w, xy.y));" - " y = (luma_chroma.b - 0.0625) * 1.1643;" + " y = luma_chroma.b;" "}" "u = luma_chroma.a - 0.5;" "v = luma_chroma.g - 0.5;" @@ -866,10 +969,10 @@ QString CaptureWinGLEngine::shader_YUY2_invariant(__u32 format) case V4L2_PIX_FMT_UYVY: return QString("if (mod(xcoord, 2.0) == 0.0) {" " luma_chroma = texture2D(tex, xy);" - " y = (luma_chroma.g - 0.0625) * 1.1643;" + " y = luma_chroma.g;" "} else {" " luma_chroma = texture2D(tex, vec2(xy.x - texl_w, xy.y));" - " y = (luma_chroma.a - 0.0625) * 1.1643;" + " y = luma_chroma.a;" "}" "u = luma_chroma.r - 0.5;" "v = luma_chroma.b - 0.5;" @@ -878,10 +981,10 @@ QString CaptureWinGLEngine::shader_YUY2_invariant(__u32 format) case V4L2_PIX_FMT_VYUY: return QString("if (mod(xcoord, 2.0) == 0.0) {" " luma_chroma = texture2D(tex, xy);" - " y = (luma_chroma.g - 0.0625) * 1.1643;" + " y = luma_chroma.g;" "} else {" " luma_chroma = texture2D(tex, vec2(xy.x - texl_w, xy.y));" - " y = (luma_chroma.a - 0.0625) * 1.1643;" + " y = luma_chroma.a;" "}" "u = luma_chroma.b - 0.5;" "v = luma_chroma.r - 0.5;" @@ -921,7 +1024,8 @@ void CaptureWinGLEngine::shader_YUY2(__u32 format) QString codeBody = shader_YUY2_invariant(format); - QString codeTail = codeYUV2RGB() + + QString codeTail = codeYUVNormalize() + + codeYUV2RGB() + codeTransformToLinear() + codeColorspaceConversion() + codeTransformToNonLinear() + @@ -965,8 +1069,9 @@ void CaptureWinGLEngine::shader_RGB() glActiveTexture(GL_TEXTURE0); configureTexture(0); - GLint internalFmt = m_colorspace == V4L2_COLORSPACE_SRGB ? - GL_SRGB8_ALPHA8 : GL_RGBA8; + GLint internalFmt = (m_quantization != V4L2_QUANTIZATION_LIM_RANGE && + m_colorspace == V4L2_COLORSPACE_SRGB) ? + GL_SRGB8_ALPHA8 : GL_RGBA8; switch (m_frameFormat) { case V4L2_PIX_FMT_ARGB555: @@ -1041,7 +1146,10 @@ void CaptureWinGLEngine::shader_RGB() QString codeTail; - if (m_colorspace != V4L2_COLORSPACE_SRGB) + if (m_quantization == V4L2_QUANTIZATION_LIM_RANGE) + codeTail += codeRGBNormalize(); + if (m_quantization == V4L2_QUANTIZATION_LIM_RANGE || + m_colorspace != V4L2_COLORSPACE_SRGB) codeTail += codeTransformToLinear(); codeTail += codeColorspaceConversion() + diff --git a/utils/qv4l2/capture-win-gl.h b/utils/qv4l2/capture-win-gl.h index a20e626c..cb842596 100644 --- a/utils/qv4l2/capture-win-gl.h +++ b/utils/qv4l2/capture-win-gl.h @@ -46,7 +46,7 @@ public: __u32 format, unsigned char *data, unsigned char *data2); bool hasNativeFormat(__u32 format); void lockSize(QSize size); - void setColorspace(unsigned colorspace); + void setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv); void setDisplayColorspace(unsigned colorspace); void setField(unsigned field); void setBlending(bool enable) { m_blending = enable; } @@ -65,6 +65,8 @@ private: void shader_RGB(); void shader_YUY2(__u32 format); QString shader_YUY2_invariant(__u32 format); + QString codeYUVNormalize(); + QString codeRGBNormalize(); QString codeYUV2RGB(); QString codeTransformToLinear(); QString codeColorspaceConversion(); @@ -88,6 +90,9 @@ private: int m_WCrop; int m_HCrop; unsigned m_colorspace; + unsigned m_ycbcr_enc; + unsigned m_quantization; + bool m_is_sdtv; unsigned m_field; unsigned m_displayColorspace; int m_screenTextureCount; @@ -115,7 +120,7 @@ public: void stop(); bool hasNativeFormat(__u32 format); static bool isSupported(); - void setColorspace(unsigned colorspace); + void setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv); void setField(unsigned field); void setDisplayColorspace(unsigned colorspace); void setBlending(bool enable); diff --git a/utils/qv4l2/capture-win-qt.h b/utils/qv4l2/capture-win-qt.h index cb477ddd..f99245d1 100644 --- a/utils/qv4l2/capture-win-qt.h +++ b/utils/qv4l2/capture-win-qt.h @@ -36,7 +36,7 @@ public: void stop(); bool hasNativeFormat(__u32 format); static bool isSupported() { return true; } - void setColorspace(unsigned colorspace) {} + void setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv) {} void setField(unsigned field) {} void setDisplayColorspace(unsigned colorspace) {} void setBlending(bool enable) {} diff --git a/utils/qv4l2/capture-win.h b/utils/qv4l2/capture-win.h index df28b331..18b5ff81 100644 --- a/utils/qv4l2/capture-win.h +++ b/utils/qv4l2/capture-win.h @@ -69,7 +69,7 @@ public: void setPixelAspectRatio(double ratio); float getHorScaleFactor(); float getVertScaleFactor(); - virtual void setColorspace(unsigned colorspace) = 0; + virtual void setColorspace(unsigned colorspace, unsigned ycbcr_enc, unsigned quantization, bool is_sdtv) = 0; virtual void setField(unsigned field) = 0; virtual void setDisplayColorspace(unsigned colorspace) = 0; virtual void setBlending(bool enable) = 0; diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp index 0b69cdef..8e132baf 100644 --- a/utils/qv4l2/general-tab.cpp +++ b/utils/qv4l2/general-tab.cpp @@ -68,6 +68,7 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren m_isSDR(false), m_isVbi(false), m_isOutput(false), + m_isSDTV(false), m_freqFac(16), m_freqRfFac(16), m_isPlanar(false), @@ -82,6 +83,8 @@ GeneralTab::GeneralTab(const QString &device, cv4l_fd *fd, int n, QWidget *paren m_videoTimings(NULL), m_pixelAspectRatio(NULL), m_colorspace(NULL), + m_ycbcrEnc(NULL), + m_quantRange(NULL), m_displayColorspace(NULL), m_cropping(NULL), m_qryTimings(NULL), @@ -341,6 +344,7 @@ void GeneralTab::inputSection(v4l2_input vin) m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft); connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int))); refreshStandards(); + m_isSDTV = true; if (query_std(tmp) != ENOTTY) { m_qryStandard = new QToolButton(parentWidget()); m_qryStandard->setIcon(QIcon(":/enterbutt.png")); @@ -525,6 +529,7 @@ void GeneralTab::outputSection(v4l2_output vout) m_stdRow->addWidget(new QLabel("TV Standard", parentWidget()), 0, 0, Qt::AlignLeft); m_stdRow->addWidget(m_tvStandard, 0, 1, Qt::AlignLeft); connect(m_tvStandard, SIGNAL(activated(int)), SLOT(standardChanged(int))); + m_isSDTV = true; refreshStandards(); } @@ -609,7 +614,7 @@ void GeneralTab::outputSection(v4l2_output vout) void GeneralTab::audioSection(v4l2_audio vaudio, v4l2_audioout vaudout) { - if (hasAlsaAudio()) { + if (hasAlsaAudio() && !m_isOutput) { if (createAudioDeviceList()) { addLabel("Audio Input Device"); connect(m_audioInDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice())); @@ -769,21 +774,48 @@ void GeneralTab::formatSection(v4l2_fmtdesc fmt) m_colorspace = new QComboBox(parentWidget()); m_colorspace->addItem("Autodetect"); m_colorspace->addItem("SMPTE 170M"); - m_colorspace->addItem("SMPTE 240M"); m_colorspace->addItem("REC 709"); + m_colorspace->addItem("sRGB"); + m_colorspace->addItem("Adobe RGB"); + m_colorspace->addItem("BT.2020 YCbCr"); + m_colorspace->addItem("SMPTE 240M"); m_colorspace->addItem("470 System M"); m_colorspace->addItem("470 System BG"); - m_colorspace->addItem("sRGB"); addLabel("Colorspace"); addWidget(m_colorspace); connect(m_colorspace, SIGNAL(activated(int)), SIGNAL(colorspaceChanged())); + m_ycbcrEnc = new QComboBox(parentWidget()); + m_ycbcrEnc->addItem("Autodetect"); + m_ycbcrEnc->addItem("ITU-R 601"); + m_ycbcrEnc->addItem("Rec. 709"); + m_ycbcrEnc->addItem("xvYCC 601"); + m_ycbcrEnc->addItem("xvYCC 709"); + m_ycbcrEnc->addItem("sYCC"); + m_ycbcrEnc->addItem("BT.2020"); + m_ycbcrEnc->addItem("BT.2020 Constant Luminance"); + m_ycbcrEnc->addItem("SMPTE 240M"); + + addLabel("Y'CbCr Encoding"); + addWidget(m_ycbcrEnc); + connect(m_ycbcrEnc, SIGNAL(activated(int)), SIGNAL(ycbcrEncChanged())); + + m_quantRange = new QComboBox(parentWidget()); + m_quantRange->addItem("Autodetect"); + m_quantRange->addItem("Full Range"); + m_quantRange->addItem("Limited Range"); + + addLabel("Quantization"); + addWidget(m_quantRange); + connect(m_quantRange, SIGNAL(activated(int)), SIGNAL(quantRangeChanged())); + m_displayColorspace = new QComboBox(parentWidget()); m_displayColorspace->addItem("sRGB"); m_displayColorspace->addItem("Linear RGB"); m_displayColorspace->addItem("REC 709"); m_displayColorspace->addItem("SMPTE 240M"); + m_displayColorspace->addItem("AdobeRGB"); addLabel("Display Colorspace"); addWidget(m_displayColorspace); @@ -930,16 +962,60 @@ unsigned GeneralTab::getColorspace() const case 1: return V4L2_COLORSPACE_SMPTE170M; case 2: - return V4L2_COLORSPACE_SMPTE240M; - case 3: return V4L2_COLORSPACE_REC709; + case 3: + default: + return V4L2_COLORSPACE_SRGB; case 4: - return V4L2_COLORSPACE_470_SYSTEM_M; + return V4L2_COLORSPACE_ADOBERGB; case 5: + return V4L2_COLORSPACE_BT2020; + case 6: + return V4L2_COLORSPACE_SMPTE240M; + case 7: + return V4L2_COLORSPACE_470_SYSTEM_M; + case 8: return V4L2_COLORSPACE_470_SYSTEM_BG; + } +} + +unsigned GeneralTab::getYCbCrEnc() const +{ + if (m_ycbcrEnc == NULL) + return V4L2_YCBCR_ENC_DEFAULT; + switch (m_ycbcrEnc->currentIndex()) { + case 1: + return V4L2_YCBCR_ENC_601; + case 2: + return V4L2_YCBCR_ENC_709; + case 3: + return V4L2_YCBCR_ENC_XV601; + case 4: + return V4L2_YCBCR_ENC_XV709; + case 5: + return V4L2_YCBCR_ENC_SYCC; case 6: + return V4L2_YCBCR_ENC_BT2020; + case 7: + return V4L2_YCBCR_ENC_BT2020_CONST_LUM; + case 8: + return V4L2_YCBCR_ENC_SMPTE240M; default: - return V4L2_COLORSPACE_SRGB; + return V4L2_YCBCR_ENC_DEFAULT; + } +} + +unsigned GeneralTab::getQuantRange() const +{ + if (m_quantRange == NULL) + return V4L2_QUANTIZATION_DEFAULT; + switch (m_quantRange->currentIndex()) { + case 1: + return V4L2_QUANTIZATION_FULL_RANGE; + case 2: + return V4L2_QUANTIZATION_LIM_RANGE; + default: + return V4L2_QUANTIZATION_DEFAULT; } } @@ -957,6 +1033,8 @@ unsigned GeneralTab::getDisplayColorspace() const return V4L2_COLORSPACE_REC709; case 3: return V4L2_COLORSPACE_SMPTE240M; + case 4: + return V4L2_COLORSPACE_ADOBERGB; } } @@ -1797,6 +1875,7 @@ void GeneralTab::updateTimings() what.sprintf("Video Timings (%u)\n" "Frame %ux%u\n", p.index, p.timings.bt.width, p.timings.bt.height); + m_isSDTV = p.timings.bt.width <= 720 && p.timings.bt.height <= 576; m_videoTimings->setStatusTip(what); m_videoTimings->setWhatsThis(what); updateVidFormat(); diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h index 6b234fdc..5affc0a1 100644 --- a/utils/qv4l2/general-tab.h +++ b/utils/qv4l2/general-tab.h @@ -72,16 +72,23 @@ public: bool isVbi() const { return m_isVbi; } bool isSlicedVbi() const; bool isPlanar() const { return m_isPlanar; } + bool isSDTV() const { return m_isSDTV; } __u32 usePrio() const { return m_recordPrio->isChecked() ? V4L2_PRIORITY_RECORD : V4L2_PRIORITY_DEFAULT; } void setHaveBuffers(bool haveBuffers); + unsigned getNumBuffers() + { + return m_numBuffers->value(); + } void sourceChange(const v4l2_event &ev); void sourceChangeSubscribe(); unsigned getDisplayColorspace() const; unsigned getColorspace() const; + unsigned getYCbCrEnc() const; + unsigned getQuantRange() const; int getWidth(); unsigned getNumBuffers() const; @@ -90,6 +97,8 @@ signals: void pixelAspectRatioChanged(); void croppingChanged(); void colorspaceChanged(); + void ycbcrEncChanged(); + void quantRangeChanged(); void clearBuffers(); void displayColorspaceChanged(); @@ -292,6 +301,7 @@ private: bool m_isSDR; bool m_isVbi; bool m_isOutput; + bool m_isSDTV; double m_freqFac; double m_freqRfFac; bool m_isPlanar; @@ -326,6 +336,8 @@ private: QComboBox *m_videoTimings; QComboBox *m_pixelAspectRatio; QComboBox *m_colorspace; + QComboBox *m_ycbcrEnc; + QComboBox *m_quantRange; QComboBox *m_displayColorspace; QComboBox *m_cropping; QToolButton *m_qryTimings; diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp index a70115b9..0784a151 100644 --- a/utils/qv4l2/qv4l2.cpp +++ b/utils/qv4l2/qv4l2.cpp @@ -271,6 +271,8 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen) connect(m_genTab, SIGNAL(pixelAspectRatioChanged()), this, SLOT(updatePixelAspectRatio())); connect(m_genTab, SIGNAL(croppingChanged()), this, SLOT(updateCropping())); connect(m_genTab, SIGNAL(colorspaceChanged()), this, SLOT(updateColorspace())); + connect(m_genTab, SIGNAL(ycbcrEncChanged()), this, SLOT(updateColorspace())); + connect(m_genTab, SIGNAL(quantRangeChanged()), this, SLOT(updateColorspace())); connect(m_genTab, SIGNAL(displayColorspaceChanged()), this, SLOT(updateDisplayColorspace())); connect(m_genTab, SIGNAL(clearBuffers()), this, SLOT(clearBuffers())); m_tabs->addTab(w, "General Settings"); @@ -1041,17 +1043,22 @@ void ApplicationWindow::updateColorspace() return; unsigned colorspace = m_genTab->getColorspace(); + unsigned ycbcrEnc = m_genTab->getYCbCrEnc(); + unsigned quantRange = m_genTab->getQuantRange(); + cv4l_fmt fmt; - if (colorspace == 0) { - cv4l_fmt fmt; + g_fmt(fmt); + // don't use the wrapped ioctl since it doesn't + // update colorspace correctly. + ::ioctl(g_fd(), VIDIOC_G_FMT, &fmt); - g_fmt(fmt); - // don't use the wrapped ioctl since it doesn't - // update colorspace correctly. - ::ioctl(g_fd(), VIDIOC_G_FMT, &fmt); + if (colorspace == 0) colorspace = fmt.g_colorspace(); - } - m_capture->setColorspace(colorspace); + if (ycbcrEnc == 0) + ycbcrEnc = fmt.g_ycbcr_enc(); + if (quantRange == 0) + quantRange = fmt.g_quantization(); + m_capture->setColorspace(colorspace, ycbcrEnc, quantRange, m_genTab->isSDTV()); } void ApplicationWindow::updateDisplayColorspace() @@ -1137,10 +1144,15 @@ void ApplicationWindow::outStart(bool start) tpg_s_rgb_range(&m_tpg, V4L2_DV_RGB_RANGE_AUTO); else tpg_s_rgb_range(&m_tpg, ctrl.value); - if (m_tpgColorspace == 0) + if (m_tpgColorspace == 0) { fmt.s_colorspace(defaultColorspace(false)); - else + fmt.s_ycbcr_enc(V4L2_YCBCR_ENC_DEFAULT); + fmt.s_flags(0); + } else { fmt.s_colorspace(m_tpgColorspace); + fmt.s_ycbcr_enc(m_tpgYCbCrEnc); + fmt.s_quantization(m_tpgQuantRange); + } s_fmt(fmt); if (out.capabilities & V4L2_OUT_CAP_STD) { @@ -1160,6 +1172,8 @@ void ApplicationWindow::outStart(bool start) } tpg_s_colorspace(&m_tpg, m_tpgColorspace ? m_tpgColorspace : fmt.g_colorspace()); + tpg_s_ycbcr_enc(&m_tpg, m_tpgColorspace ? m_tpgYCbCrEnc : fmt.g_ycbcr_enc()); + tpg_s_quantization(&m_tpg, m_tpgColorspace ? m_tpgQuantRange : fmt.g_quantization()); tpg_s_bytesperline(&m_tpg, 0, fmt.g_bytesperline(0)); tpg_s_bytesperline(&m_tpg, 1, fmt.g_bytesperline(1)); if (m_capMethod == methodRead) @@ -1216,7 +1230,8 @@ void ApplicationWindow::capStart(bool start) QImage::Format dstFmt = QImage::Format_RGB888; struct v4l2_fract interval; __u32 width, height, pixfmt; - unsigned colorspace, field; + unsigned colorspace, ycbcr_enc, field; + unsigned quantization; if (!start) { stopStreaming(); @@ -1347,6 +1362,8 @@ void ApplicationWindow::capStart(bool start) height = m_capSrcFormat.g_height(); pixfmt = m_capSrcFormat.g_pixelformat(); colorspace = m_capSrcFormat.g_colorspace(); + ycbcr_enc = m_capSrcFormat.g_ycbcr_enc(); + quantization = m_capSrcFormat.g_quantization(); field = m_capSrcFormat.g_field(); m_mustConvert = false; } else { @@ -1367,6 +1384,8 @@ void ApplicationWindow::capStart(bool start) height = m_capDestFormat.g_height(); pixfmt = m_capDestFormat.g_pixelformat(); colorspace = m_capDestFormat.g_colorspace(); + ycbcr_enc = m_capDestFormat.g_ycbcr_enc(); + quantization = m_capDestFormat.g_quantization(); field = m_capDestFormat.g_field(); } @@ -1387,9 +1406,12 @@ void ApplicationWindow::capStart(bool start) m_capImage->fill(0); updatePixelAspectRatio(); - if (m_genTab->getColorspace()) + if (m_genTab->getColorspace()) { colorspace = m_genTab->getColorspace(); - m_capture->setColorspace(colorspace); + ycbcr_enc = m_genTab->getYCbCrEnc(); + quantization = m_genTab->getQuantRange(); + } + m_capture->setColorspace(colorspace, ycbcr_enc, quantization, m_genTab->isSDTV()); m_capture->setField(field); m_capture->setDisplayColorspace(m_genTab->getDisplayColorspace()); diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h index 9575ecd4..74b28688 100644 --- a/utils/qv4l2/qv4l2.h +++ b/utils/qv4l2/qv4l2.h @@ -173,6 +173,8 @@ private slots: void insEAVChanged(int val); void videoAspectRatioChanged(int val); void colorspaceChanged(int val); + void ycbcrEncodingChanged(int val); + void quantRangeChanged(int val); void limRGBRangeChanged(int val); void fillPercentageChanged(int val); void alphaComponentChanged(int val); @@ -234,6 +236,8 @@ private: unsigned m_tpgField; unsigned m_tpgSizeImage; unsigned m_tpgColorspace; + unsigned m_tpgYCbCrEnc; + unsigned m_tpgQuantRange; bool m_useTpg; QCheckBox *m_tpgLimRGBRange; diff --git a/utils/qv4l2/tpg-tab.cpp b/utils/qv4l2/tpg-tab.cpp index 8ef1bbe9..c4f403e2 100644 --- a/utils/qv4l2/tpg-tab.cpp +++ b/utils/qv4l2/tpg-tab.cpp @@ -131,9 +131,35 @@ void ApplicationWindow::addTpgTab(int m_winWidth) combo->addItem("470 System M"); combo->addItem("470 System BG"); combo->addItem("sRGB"); + combo->addItem("Adobe RGB"); + combo->addItem("BT.2020"); addWidget(grid, combo); connect(combo, SIGNAL(activated(int)), SLOT(colorspaceChanged(int))); + m_tpgYCbCrEnc = 0; + addLabel(grid, "Y'CbCr Encoding"); + combo = new QComboBox(w); + combo->addItem("Default"); + combo->addItem("ITU-R 601"); + combo->addItem("Rec. 709"); + combo->addItem("xvYCC 601"); + combo->addItem("xvYCC 709"); + combo->addItem("sYCC"); + combo->addItem("BT.2020"); + combo->addItem("BT.2020 Constant Luminance"); + combo->addItem("SMPTE 240M"); + addWidget(grid, combo); + connect(combo, SIGNAL(activated(int)), SLOT(ycbcrEncodingChanged(int))); + + m_tpgQuantRange = 0; + addLabel(grid, "Quantization Range"); + combo = new QComboBox(w); + combo->addItem("Default"); + combo->addItem("Full Range"); + combo->addItem("Limited Range"); + addWidget(grid, combo); + connect(combo, SIGNAL(activated(int)), SLOT(quantRangeChanged(int))); + addLabel(grid, "Show Border"); check = new QCheckBox(w); addWidget(grid, check); @@ -214,6 +240,12 @@ void ApplicationWindow::vertMovementChanged(int val) tpg_s_mv_vert_mode(&m_tpg, (tpg_move_mode)val); } +void ApplicationWindow::quantRangeChanged(int val) +{ + m_tpgQuantRange = val; + tpg_s_quantization(&m_tpg, val); +} + void ApplicationWindow::showBorderChanged(int val) { tpg_s_show_border(&m_tpg, val); @@ -321,6 +353,12 @@ void ApplicationWindow::colorspaceChanged(int val) case 5: m_tpgColorspace = V4L2_COLORSPACE_470_SYSTEM_BG; break; + case 7: + m_tpgColorspace = V4L2_COLORSPACE_ADOBERGB; + break; + case 8: + m_tpgColorspace = V4L2_COLORSPACE_BT2020; + break; case 6: default: m_tpgColorspace = V4L2_COLORSPACE_SRGB; @@ -338,8 +376,64 @@ void ApplicationWindow::colorspaceChanged(int val) fmt.s_colorspace(defaultColorspace(false)); else fmt.s_colorspace(m_tpgColorspace); + fmt.s_ycbcr_enc(m_tpgYCbCrEnc); + fmt.s_quantization(tpg_g_quantization(&m_tpg)); + s_fmt(fmt); + tpg_s_colorspace(&m_tpg, m_tpgColorspace ? m_tpgColorspace : fmt.g_colorspace()); + tpg_s_ycbcr_enc(&m_tpg, m_tpgColorspace ? m_tpgYCbCrEnc : fmt.g_ycbcr_enc()); + tpg_s_quantization(&m_tpg, m_tpgColorspace ? m_tpgQuantRange : fmt.g_quantization()); +} + +void ApplicationWindow::ycbcrEncodingChanged(int val) +{ + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_DEFAULT; + switch (val) { + case 0: + default: + break; + case 1: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_601; + break; + case 2: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_709; + break; + case 3: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_XV601; + break; + case 4: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_XV709; + break; + case 5: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_SYCC; + break; + case 6: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_BT2020; + break; + case 7: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_BT2020_CONST_LUM; + break; + case 8: + m_tpgYCbCrEnc = V4L2_YCBCR_ENC_SMPTE240M; + break; + } + + cv4l_fmt fmt; + v4l2_output out; + + g_output(out.index); + enum_output(out, true, out.index); + + g_fmt(fmt); + if (m_tpgColorspace == 0) + fmt.s_colorspace(defaultColorspace(false)); + else + fmt.s_colorspace(m_tpgColorspace); + fmt.s_ycbcr_enc(m_tpgYCbCrEnc); + fmt.s_quantization(tpg_g_quantization(&m_tpg)); s_fmt(fmt); tpg_s_colorspace(&m_tpg, m_tpgColorspace ? m_tpgColorspace : fmt.g_colorspace()); + tpg_s_ycbcr_enc(&m_tpg, m_tpgColorspace ? m_tpgYCbCrEnc : fmt.g_ycbcr_enc()); + tpg_s_quantization(&m_tpg, m_tpgColorspace ? m_tpgQuantRange : fmt.g_quantization()); } void ApplicationWindow::limRGBRangeChanged(int val) |