diff --git a/sdk/android/api/org/webrtc/YuvConverter.java b/sdk/android/api/org/webrtc/YuvConverter.java index de02c11c3b..7d55f07518 100644 --- a/sdk/android/api/org/webrtc/YuvConverter.java +++ b/sdk/android/api/org/webrtc/YuvConverter.java @@ -46,12 +46,24 @@ public class YuvConverter { + "}\n"; private static class ShaderCallbacks implements GlGenericDrawer.ShaderCallbacks { - // Y'UV444 to RGB888, see https://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion. We - // use the ITU-R coefficients for U and V. - private static final float[] yCoeffs = new float[] {0.2987856f, 0.5871095f, 0.1141049f, 0.0f}; + // Y'UV444 to RGB888, see https://en.wikipedia.org/wiki/YUV#Y%E2%80%B2UV444_to_RGB888_conversion + // We use the ITU-R BT.601 coefficients for Y, U and V. + // The values in Wikipedia are inaccurate, the accurate values derived from the spec are: + // Y = 0.299 * R + 0.587 * G + 0.114 * B + // U = -0.168736 * R - 0.331264 * G + 0.5 * B + 0.5 + // V = 0.5 * R - 0.418688 * G - 0.0813124 * B + 0.5 + // To map the Y-values to range [16-235] and U- and V-values to range [16-240], the matrix has + // been multiplied with matrix: + // {{219 / 255, 0, 0, 16 / 255}, + // {0, 224 / 255, 0, 16 / 255}, + // {0, 0, 224 / 255, 16 / 255}, + // {0, 0, 0, 1}} + private static final float[] yCoeffs = + new float[] {0.256788f, 0.504129f, 0.0979059f, 0.0627451f}; private static final float[] uCoeffs = - new float[] {-0.168805420f, -0.3317003f, 0.5005057f, 0.5f}; - private static final float[] vCoeffs = new float[] {0.4997964f, -0.4184672f, -0.0813292f, 0.5f}; + new float[] {-0.148223f, -0.290993f, 0.439216f, 0.501961f}; + private static final float[] vCoeffs = + new float[] {0.439216f, -0.367788f, -0.0714274f, 0.501961f}; private int xUnitLoc; private int coeffsLoc; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/EglRendererTest.java b/sdk/android/instrumentationtests/src/org/webrtc/EglRendererTest.java index 29d822face..fe2a86361d 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/EglRendererTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/EglRendererTest.java @@ -47,18 +47,18 @@ public class EglRendererTest { private final static byte[][][] TEST_FRAMES_DATA = { { new byte[] { - 11, -12, 13, -14, -15, 16, -17, 18, 19, -110, 111, -112, -113, 114, -115, 116}, - new byte[] {117, 118, 119, 120}, new byte[] {121, 122, 123, 124}, + -99, -93, -88, -83, -78, -73, -68, -62, -56, -52, -46, -41, -36, -31, -26, -20}, + new byte[] {110, 113, 116, 118}, new byte[] {31, 45, 59, 73}, }, { - new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112, -113, -114, - -115, -116}, - new byte[] {-121, -122, -123, -124}, new byte[] {-117, -118, -119, -120}, + new byte[] { + -108, -103, -98, -93, -87, -82, -77, -72, -67, -62, -56, -50, -45, -40, -35, -30}, + new byte[] {120, 123, 125, -127}, new byte[] {87, 100, 114, 127}, }, { - new byte[] {-11, -12, -13, -14, -15, -16, -17, -18, -19, -110, -111, -112, -113, -114, - -115, -116}, - new byte[] {117, 118, 119, 120}, new byte[] {121, 122, 123, 124}, + new byte[] { + -117, -112, -107, -102, -97, -92, -87, -81, -75, -71, -65, -60, -55, -50, -44, -39}, + new byte[] {113, 116, 118, 120}, new byte[] {45, 59, 73, 87}, }, }; private final static ByteBuffer[][] TEST_FRAMES = @@ -176,7 +176,7 @@ public class EglRendererTest { float highYValue = (plane.get(highIndexY * stride + lowIndexX) & 0xFF) * lowWeightX + (plane.get(highIndexY * stride + highIndexX) & 0xFF) * highWeightX; - return (lowWeightY * lowYValue + highWeightY * highYValue) / 255f; + return lowWeightY * lowYValue + highWeightY * highYValue; } private static byte saturatedFloatToByte(float c) { @@ -200,15 +200,16 @@ public class EglRendererTest { for (int y = 0; y < TEST_FRAME_HEIGHT; y++) { for (int x = 0; x < TEST_FRAME_WIDTH; x++) { - final int x2 = x / 2; - final int y2 = y / 2; - - final float yC = (yuvFrame[0].get(y * yStride + x) & 0xFF) / 255f; - final float uC = linearSample(yuvFrame[1], TEST_FRAME_WIDTH / 2, TEST_FRAME_HEIGHT / 2, - (x + 0.5f) / TEST_FRAME_WIDTH, (y + 0.5f) / TEST_FRAME_HEIGHT) + final float yC = ((yuvFrame[0].get(y * yStride + x) & 0xFF) - 16f) / 219f; + final float uC = (linearSample(yuvFrame[1], TEST_FRAME_WIDTH / 2, TEST_FRAME_HEIGHT / 2, + (x + 0.5f) / TEST_FRAME_WIDTH, (y + 0.5f) / TEST_FRAME_HEIGHT) + - 16f) + / 224f - 0.5f; - final float vC = linearSample(yuvFrame[2], TEST_FRAME_WIDTH / 2, TEST_FRAME_HEIGHT / 2, - (x + 0.5f) / TEST_FRAME_WIDTH, (y + 0.5f) / TEST_FRAME_HEIGHT) + final float vC = (linearSample(yuvFrame[2], TEST_FRAME_WIDTH / 2, TEST_FRAME_HEIGHT / 2, + (x + 0.5f) / TEST_FRAME_WIDTH, (y + 0.5f) / TEST_FRAME_HEIGHT) + - 16f) + / 224f - 0.5f; final float rC = yC + 1.403f * vC; final float gC = yC - 0.344f * uC - 0.714f * vC; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/GlRectDrawerTest.java b/sdk/android/instrumentationtests/src/org/webrtc/GlRectDrawerTest.java index fecddb34fe..7d5196e91e 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/GlRectDrawerTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/GlRectDrawerTest.java @@ -181,15 +181,17 @@ public class GlRectDrawerTest { for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { // YUV color space. Y in [0, 1], UV in [-0.5, 0.5]. The constants are taken from the YUV - // fragment shader code in GlRectDrawer. + // fragment shader code in GlGenericDrawer. final float y_luma = normalizedByte(yuvPlanes[0].get()); - final float u_chroma = normalizedByte(yuvPlanes[1].get()) - 0.5f; - final float v_chroma = normalizedByte(yuvPlanes[2].get()) - 0.5f; + final float u_chroma = normalizedByte(yuvPlanes[1].get()); + final float v_chroma = normalizedByte(yuvPlanes[2].get()); // Expected color in unrounded RGB [0.0f, 255.0f]. - final float expectedRed = saturatedConvert(y_luma + 1.403f * v_chroma); - final float expectedGreen = - saturatedConvert(y_luma - 0.344f * u_chroma - 0.714f * v_chroma); - final float expectedBlue = saturatedConvert(y_luma + 1.77f * u_chroma); + final float expectedRed = + saturatedConvert(1.16438f * y_luma + 1.59603f * v_chroma - 0.874202f); + final float expectedGreen = saturatedConvert( + 1.16438f * y_luma - 0.391762f * u_chroma - 0.812968f * v_chroma + 0.531668f); + final float expectedBlue = + saturatedConvert(1.16438f * y_luma + 2.01723f * u_chroma - 1.08563f); // Actual color in RGB8888. final int actualRed = data.get() & 0xFF; diff --git a/sdk/android/instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java b/sdk/android/instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java index 3cd08cc8b6..837bd6ee41 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java @@ -459,9 +459,9 @@ public class SurfaceTextureHelperTest { final int green[] = new int[] {66, 210, 162}; final int blue[] = new int[] {161, 117, 158}; - final int ref_y[] = new int[] {81, 180, 168}; - final int ref_u[] = new int[] {173, 93, 122}; - final int ref_v[] = new int[] {127, 103, 140}; + final int ref_y[] = new int[] {85, 170, 161}; + final int ref_u[] = new int[] {168, 97, 123}; + final int ref_v[] = new int[] {127, 106, 138}; // Draw three frames. for (int i = 0; i < 3; ++i) { diff --git a/sdk/android/instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java b/sdk/android/instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java index 539ba3ddf5..afbb0fd115 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java @@ -103,28 +103,28 @@ public class VideoFrameBufferTest { public static VideoFrame.I420Buffer createTestI420Buffer() { final int width = 16; final int height = 16; - final int[] yData = new int[] {164, 170, 176, 182, 188, 194, 200, 206, 213, 218, 225, 231, 237, - 243, 249, 255, 153, 159, 165, 171, 177, 183, 189, 195, 201, 207, 213, 220, 226, 232, 238, - 244, 142, 148, 154, 160, 166, 172, 178, 184, 191, 196, 203, 209, 215, 221, 227, 233, 131, - 137, 143, 149, 155, 161, 167, 174, 180, 186, 192, 198, 204, 210, 216, 222, 120, 126, 132, - 138, 145, 151, 157, 163, 169, 175, 181, 187, 193, 199, 205, 211, 109, 115, 121, 128, 133, - 140, 146, 152, 158, 164, 170, 176, 182, 188, 194, 200, 99, 104, 111, 117, 123, 129, 135, - 141, 147, 153, 159, 165, 171, 177, 183, 189, 87, 94, 100, 106, 112, 118, 124, 130, 136, 142, - 148, 154, 160, 166, 172, 178, 77, 83, 89, 95, 101, 107, 113, 119, 125, 131, 137, 143, 149, - 155, 161, 167, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138, 144, 150, 156, 55, - 61, 67, 73, 79, 85, 91, 97, 103, 109, 115, 121, 127, 133, 139, 145, 44, 50, 56, 62, 68, 74, - 80, 86, 92, 98, 104, 110, 116, 122, 128, 134, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, - 99, 105, 111, 117, 123, 22, 28, 34, 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106, 113, - 11, 17, 23, 29, 35, 41, 47, 53, 59, 65, 71, 77, 83, 89, 96, 102, 0, 6, 12, 18, 24, 30, 36, - 42, 48, 54, 60, 66, 72, 79, 85, 91}; - final int[] uData = new int[] {108, 111, 114, 117, 119, 122, 125, 128, 111, 114, 117, 119, 122, - 125, 128, 130, 114, 117, 119, 122, 125, 128, 130, 133, 117, 119, 122, 125, 128, 130, 133, - 136, 119, 122, 125, 128, 130, 133, 136, 139, 122, 125, 128, 130, 133, 136, 139, 141, 125, - 128, 130, 133, 136, 139, 141, 144, 128, 130, 133, 136, 139, 141, 144, 147}; - final int[] vData = new int[] {18, 34, 49, 65, 81, 96, 112, 127, 34, 49, 65, 81, 96, 112, 128, - 143, 49, 65, 81, 96, 112, 127, 143, 159, 65, 81, 96, 112, 127, 143, 159, 174, 81, 96, 112, - 128, 143, 159, 174, 190, 96, 112, 128, 143, 159, 174, 190, 206, 112, 127, 143, 159, 174, - 190, 205, 221, 127, 143, 159, 174, 190, 205, 221, 237}; + final int[] yData = new int[] {156, 162, 167, 172, 177, 182, 187, 193, 199, 203, 209, 214, 219, + 224, 229, 235, 147, 152, 157, 162, 168, 173, 178, 183, 188, 193, 199, 205, 210, 215, 220, + 225, 138, 143, 148, 153, 158, 163, 168, 174, 180, 184, 190, 195, 200, 205, 211, 216, 128, + 133, 138, 144, 149, 154, 159, 165, 170, 175, 181, 186, 191, 196, 201, 206, 119, 124, 129, + 134, 140, 145, 150, 156, 161, 166, 171, 176, 181, 187, 192, 197, 109, 114, 119, 126, 130, + 136, 141, 146, 151, 156, 162, 167, 172, 177, 182, 187, 101, 105, 111, 116, 121, 126, 132, + 137, 142, 147, 152, 157, 162, 168, 173, 178, 90, 96, 101, 107, 112, 117, 122, 127, 132, 138, + 143, 148, 153, 158, 163, 168, 82, 87, 92, 97, 102, 107, 113, 118, 123, 128, 133, 138, 144, + 149, 154, 159, 72, 77, 83, 88, 93, 98, 103, 108, 113, 119, 124, 129, 134, 139, 144, 150, 63, + 68, 73, 78, 83, 89, 94, 99, 104, 109, 114, 119, 125, 130, 135, 140, 53, 58, 64, 69, 74, 79, + 84, 89, 95, 100, 105, 110, 115, 120, 126, 131, 44, 49, 54, 59, 64, 70, 75, 80, 85, 90, 95, + 101, 106, 111, 116, 121, 34, 40, 45, 50, 55, 60, 65, 71, 76, 81, 86, 91, 96, 101, 107, 113, + 25, 30, 35, 40, 46, 51, 56, 61, 66, 71, 77, 82, 87, 92, 98, 103, 16, 21, 26, 31, 36, 41, 46, + 52, 57, 62, 67, 72, 77, 83, 89, 94}; + final int[] uData = new int[] {110, 113, 116, 118, 120, 123, 125, 128, 113, 116, 118, 120, 123, + 125, 128, 130, 116, 118, 120, 123, 125, 128, 130, 132, 118, 120, 123, 125, 128, 130, 132, + 135, 120, 123, 125, 128, 130, 132, 135, 138, 123, 125, 128, 130, 132, 135, 138, 139, 125, + 128, 130, 132, 135, 138, 139, 142, 128, 130, 132, 135, 138, 139, 142, 145}; + final int[] vData = new int[] {31, 45, 59, 73, 87, 100, 114, 127, 45, 59, 73, 87, 100, 114, 128, + 141, 59, 73, 87, 100, 114, 127, 141, 155, 73, 87, 100, 114, 127, 141, 155, 168, 87, 100, + 114, 128, 141, 155, 168, 182, 100, 114, 128, 141, 155, 168, 182, 197, 114, 127, 141, 155, + 168, 182, 196, 210, 127, 141, 155, 168, 182, 196, 210, 224}; return JavaI420Buffer.wrap(width, height, toByteBuffer(yData), /* strideY= */ width, toByteBuffer(uData), /* strideU= */ width / 2, toByteBuffer(vData), /* strideV= */ width / 2, diff --git a/sdk/android/src/java/org/webrtc/GlGenericDrawer.java b/sdk/android/src/java/org/webrtc/GlGenericDrawer.java index cd7b1e8c77..20d71c0cc3 100644 --- a/sdk/android/src/java/org/webrtc/GlGenericDrawer.java +++ b/sdk/android/src/java/org/webrtc/GlGenericDrawer.java @@ -102,12 +102,14 @@ class GlGenericDrawer implements RendererCommon.GlDrawer { stringBuilder.append("uniform sampler2D v_tex;\n"); // Add separate function for sampling texture. + // yuv_to_rgb_mat is inverse of the matrix defined in YuvConverter. stringBuilder.append("vec4 sample(vec2 p) {\n"); - stringBuilder.append(" float y = texture2D(y_tex, p).r;\n"); - stringBuilder.append(" float u = texture2D(u_tex, p).r - 0.5;\n"); - stringBuilder.append(" float v = texture2D(v_tex, p).r - 0.5;\n"); - stringBuilder.append( - " return vec4(y + 1.403 * v, y - 0.344 * u - 0.714 * v, y + 1.77 * u, 1);\n"); + stringBuilder.append(" float y = texture2D(y_tex, p).r * 1.16438;\n"); + stringBuilder.append(" float u = texture2D(u_tex, p).r;\n"); + stringBuilder.append(" float v = texture2D(v_tex, p).r;\n"); + stringBuilder.append(" return vec4(y + 1.59603 * v - 0.874202,\n"); + stringBuilder.append(" y - 0.391762 * u - 0.812968 * v + 0.531668,\n"); + stringBuilder.append(" y + 2.01723 * u - 1.08563, 1);\n"); stringBuilder.append("}\n"); stringBuilder.append(genericFragmentSource); } else { diff --git a/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java b/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java index 06b53672a0..1f92b9c426 100644 --- a/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java +++ b/sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java @@ -104,10 +104,12 @@ public class GlGenericDrawerTest { + "uniform sampler2D u_tex;\n" + "uniform sampler2D v_tex;\n" + "vec4 sample(vec2 p) {\n" - + " float y = texture2D(y_tex, p).r;\n" - + " float u = texture2D(u_tex, p).r - 0.5;\n" - + " float v = texture2D(v_tex, p).r - 0.5;\n" - + " return vec4(y + 1.403 * v, y - 0.344 * u - 0.714 * v, y + 1.77 * u, 1);\n" + + " float y = texture2D(y_tex, p).r * 1.16438;\n" + + " float u = texture2D(u_tex, p).r;\n" + + " float v = texture2D(v_tex, p).r;\n" + + " return vec4(y + 1.59603 * v - 0.874202,\n" + + " y - 0.391762 * u - 0.812968 * v + 0.531668,\n" + + " y + 2.01723 * u - 1.08563, 1);\n" + "}\n" + "void main() {\n" + " gl_FragColor = sample(tc);\n"