1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <GLES2/gl2.h> 12 #include <GLES2/gl2ext.h> 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 #include "webrtc/modules/video_render/android/video_render_opengles20.h" 18 19 //#define ANDROID_LOG 20 21 #ifdef ANDROID_LOG 22 #include <android/log.h> 23 #include <stdio.h> 24 25 #undef WEBRTC_TRACE 26 #define WEBRTC_TRACE(a,b,c,...) __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTCN*", __VA_ARGS__) 27 #else 28 #include "webrtc/system_wrappers/include/trace.h" 29 #endif 30 31 namespace webrtc { 32 33 const char VideoRenderOpenGles20::g_indices[] = { 0, 3, 2, 0, 2, 1 }; 34 35 const char VideoRenderOpenGles20::g_vertextShader[] = { 36 "attribute vec4 aPosition;\n" 37 "attribute vec2 aTextureCoord;\n" 38 "varying vec2 vTextureCoord;\n" 39 "void main() {\n" 40 " gl_Position = aPosition;\n" 41 " vTextureCoord = aTextureCoord;\n" 42 "}\n" }; 43 44 // The fragment shader. 45 // Do YUV to RGB565 conversion. 46 const char VideoRenderOpenGles20::g_fragmentShader[] = { 47 "precision mediump float;\n" 48 "uniform sampler2D Ytex;\n" 49 "uniform sampler2D Utex,Vtex;\n" 50 "varying vec2 vTextureCoord;\n" 51 "void main(void) {\n" 52 " float nx,ny,r,g,b,y,u,v;\n" 53 " mediump vec4 txl,ux,vx;" 54 " nx=vTextureCoord[0];\n" 55 " ny=vTextureCoord[1];\n" 56 " y=texture2D(Ytex,vec2(nx,ny)).r;\n" 57 " u=texture2D(Utex,vec2(nx,ny)).r;\n" 58 " v=texture2D(Vtex,vec2(nx,ny)).r;\n" 59 60 //" y = v;\n"+ 61 " y=1.1643*(y-0.0625);\n" 62 " u=u-0.5;\n" 63 " v=v-0.5;\n" 64 65 " r=y+1.5958*v;\n" 66 " g=y-0.39173*u-0.81290*v;\n" 67 " b=y+2.017*u;\n" 68 " gl_FragColor=vec4(r,g,b,1.0);\n" 69 "}\n" }; 70 71 VideoRenderOpenGles20::VideoRenderOpenGles20(int32_t id) : 72 _id(id), 73 _textureWidth(-1), 74 _textureHeight(-1) { 75 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", 76 __FUNCTION__, (int) _id); 77 78 const GLfloat vertices[20] = { 79 // X, Y, Z, U, V 80 -1, -1, 0, 0, 1, // Bottom Left 81 1, -1, 0, 1, 1, //Bottom Right 82 1, 1, 0, 1, 0, //Top Right 83 -1, 1, 0, 0, 0 }; //Top Left 84 85 memcpy(_vertices, vertices, sizeof(_vertices)); 86 } 87 88 VideoRenderOpenGles20::~VideoRenderOpenGles20() { 89 } 90 91 int32_t VideoRenderOpenGles20::Setup(int32_t width, int32_t height) { 92 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, 93 "%s: width %d, height %d", __FUNCTION__, (int) width, 94 (int) height); 95 96 printGLString("Version", GL_VERSION); 97 printGLString("Vendor", GL_VENDOR); 98 printGLString("Renderer", GL_RENDERER); 99 printGLString("Extensions", GL_EXTENSIONS); 100 101 int maxTextureImageUnits[2]; 102 int maxTextureSize[2]; 103 glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, maxTextureImageUnits); 104 glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxTextureSize); 105 106 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, 107 "%s: number of textures %d, size %d", __FUNCTION__, 108 (int) maxTextureImageUnits[0], (int) maxTextureSize[0]); 109 110 _program = createProgram(g_vertextShader, g_fragmentShader); 111 if (!_program) { 112 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 113 "%s: Could not create program", __FUNCTION__); 114 return -1; 115 } 116 117 int positionHandle = glGetAttribLocation(_program, "aPosition"); 118 checkGlError("glGetAttribLocation aPosition"); 119 if (positionHandle == -1) { 120 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 121 "%s: Could not get aPosition handle", __FUNCTION__); 122 return -1; 123 } 124 125 int textureHandle = glGetAttribLocation(_program, "aTextureCoord"); 126 checkGlError("glGetAttribLocation aTextureCoord"); 127 if (textureHandle == -1) { 128 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 129 "%s: Could not get aTextureCoord handle", __FUNCTION__); 130 return -1; 131 } 132 133 // set the vertices array in the shader 134 // _vertices contains 4 vertices with 5 coordinates. 135 // 3 for (xyz) for the vertices and 2 for the texture 136 glVertexAttribPointer(positionHandle, 3, GL_FLOAT, false, 137 5 * sizeof(GLfloat), _vertices); 138 checkGlError("glVertexAttribPointer aPosition"); 139 140 glEnableVertexAttribArray(positionHandle); 141 checkGlError("glEnableVertexAttribArray positionHandle"); 142 143 // set the texture coordinate array in the shader 144 // _vertices contains 4 vertices with 5 coordinates. 145 // 3 for (xyz) for the vertices and 2 for the texture 146 glVertexAttribPointer(textureHandle, 2, GL_FLOAT, false, 5 147 * sizeof(GLfloat), &_vertices[3]); 148 checkGlError("glVertexAttribPointer maTextureHandle"); 149 glEnableVertexAttribArray(textureHandle); 150 checkGlError("glEnableVertexAttribArray textureHandle"); 151 152 glUseProgram(_program); 153 int i = glGetUniformLocation(_program, "Ytex"); 154 checkGlError("glGetUniformLocation"); 155 glUniform1i(i, 0); /* Bind Ytex to texture unit 0 */ 156 checkGlError("glUniform1i Ytex"); 157 158 i = glGetUniformLocation(_program, "Utex"); 159 checkGlError("glGetUniformLocation Utex"); 160 glUniform1i(i, 1); /* Bind Utex to texture unit 1 */ 161 checkGlError("glUniform1i Utex"); 162 163 i = glGetUniformLocation(_program, "Vtex"); 164 checkGlError("glGetUniformLocation"); 165 glUniform1i(i, 2); /* Bind Vtex to texture unit 2 */ 166 checkGlError("glUniform1i"); 167 168 glViewport(0, 0, width, height); 169 checkGlError("glViewport"); 170 return 0; 171 } 172 173 // SetCoordinates 174 // Sets the coordinates where the stream shall be rendered. 175 // Values must be between 0 and 1. 176 int32_t VideoRenderOpenGles20::SetCoordinates(int32_t zOrder, 177 const float left, 178 const float top, 179 const float right, 180 const float bottom) { 181 if ((top > 1 || top < 0) || (right > 1 || right < 0) || 182 (bottom > 1 || bottom < 0) || (left > 1 || left < 0)) { 183 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 184 "%s: Wrong coordinates", __FUNCTION__); 185 return -1; 186 } 187 188 // X, Y, Z, U, V 189 // -1, -1, 0, 0, 1, // Bottom Left 190 // 1, -1, 0, 1, 1, //Bottom Right 191 // 1, 1, 0, 1, 0, //Top Right 192 // -1, 1, 0, 0, 0 //Top Left 193 194 // Bottom Left 195 _vertices[0] = (left * 2) - 1; 196 _vertices[1] = -1 * (2 * bottom) + 1; 197 _vertices[2] = zOrder; 198 199 //Bottom Right 200 _vertices[5] = (right * 2) - 1; 201 _vertices[6] = -1 * (2 * bottom) + 1; 202 _vertices[7] = zOrder; 203 204 //Top Right 205 _vertices[10] = (right * 2) - 1; 206 _vertices[11] = -1 * (2 * top) + 1; 207 _vertices[12] = zOrder; 208 209 //Top Left 210 _vertices[15] = (left * 2) - 1; 211 _vertices[16] = -1 * (2 * top) + 1; 212 _vertices[17] = zOrder; 213 214 return 0; 215 } 216 217 int32_t VideoRenderOpenGles20::Render(const VideoFrame& frameToRender) { 218 if (frameToRender.IsZeroSize()) { 219 return -1; 220 } 221 222 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d", 223 __FUNCTION__, (int) _id); 224 225 glUseProgram(_program); 226 checkGlError("glUseProgram"); 227 228 if (_textureWidth != (GLsizei) frameToRender.width() || 229 _textureHeight != (GLsizei) frameToRender.height()) { 230 SetupTextures(frameToRender); 231 } 232 UpdateTextures(frameToRender); 233 234 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, g_indices); 235 checkGlError("glDrawArrays"); 236 237 return 0; 238 } 239 240 GLuint VideoRenderOpenGles20::loadShader(GLenum shaderType, 241 const char* pSource) { 242 GLuint shader = glCreateShader(shaderType); 243 if (shader) { 244 glShaderSource(shader, 1, &pSource, NULL); 245 glCompileShader(shader); 246 GLint compiled = 0; 247 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 248 if (!compiled) { 249 GLint infoLen = 0; 250 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 251 if (infoLen) { 252 char* buf = (char*) malloc(infoLen); 253 if (buf) { 254 glGetShaderInfoLog(shader, infoLen, NULL, buf); 255 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 256 "%s: Could not compile shader %d: %s", 257 __FUNCTION__, shaderType, buf); 258 free(buf); 259 } 260 glDeleteShader(shader); 261 shader = 0; 262 } 263 } 264 } 265 return shader; 266 } 267 268 GLuint VideoRenderOpenGles20::createProgram(const char* pVertexSource, 269 const char* pFragmentSource) { 270 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); 271 if (!vertexShader) { 272 return 0; 273 } 274 275 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); 276 if (!pixelShader) { 277 return 0; 278 } 279 280 GLuint program = glCreateProgram(); 281 if (program) { 282 glAttachShader(program, vertexShader); 283 checkGlError("glAttachShader"); 284 glAttachShader(program, pixelShader); 285 checkGlError("glAttachShader"); 286 glLinkProgram(program); 287 GLint linkStatus = GL_FALSE; 288 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 289 if (linkStatus != GL_TRUE) { 290 GLint bufLength = 0; 291 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); 292 if (bufLength) { 293 char* buf = (char*) malloc(bufLength); 294 if (buf) { 295 glGetProgramInfoLog(program, bufLength, NULL, buf); 296 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 297 "%s: Could not link program: %s", 298 __FUNCTION__, buf); 299 free(buf); 300 } 301 } 302 glDeleteProgram(program); 303 program = 0; 304 } 305 } 306 return program; 307 } 308 309 void VideoRenderOpenGles20::printGLString(const char *name, GLenum s) { 310 const char *v = (const char *) glGetString(s); 311 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "GL %s = %s\n", 312 name, v); 313 } 314 315 void VideoRenderOpenGles20::checkGlError(const char* op) { 316 #ifdef ANDROID_LOG 317 for (GLint error = glGetError(); error; error = glGetError()) { 318 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 319 "after %s() glError (0x%x)\n", op, error); 320 } 321 #else 322 return; 323 #endif 324 } 325 326 static void InitializeTexture(int name, int id, int width, int height) { 327 glActiveTexture(name); 328 glBindTexture(GL_TEXTURE_2D, id); 329 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 330 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 331 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 332 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 333 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, 334 GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL); 335 } 336 337 void VideoRenderOpenGles20::SetupTextures(const VideoFrame& frameToRender) { 338 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, 339 "%s: width %d, height %d", __FUNCTION__, 340 frameToRender.width(), frameToRender.height()); 341 342 const GLsizei width = frameToRender.width(); 343 const GLsizei height = frameToRender.height(); 344 345 glGenTextures(3, _textureIds); //Generate the Y, U and V texture 346 InitializeTexture(GL_TEXTURE0, _textureIds[0], width, height); 347 InitializeTexture(GL_TEXTURE1, _textureIds[1], width / 2, height / 2); 348 InitializeTexture(GL_TEXTURE2, _textureIds[2], width / 2, height / 2); 349 350 checkGlError("SetupTextures"); 351 352 _textureWidth = width; 353 _textureHeight = height; 354 } 355 356 // Uploads a plane of pixel data, accounting for stride != width*bpp. 357 static void GlTexSubImage2D(GLsizei width, GLsizei height, int stride, 358 const uint8_t* plane) { 359 if (stride == width) { 360 // Yay! We can upload the entire plane in a single GL call. 361 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, 362 GL_UNSIGNED_BYTE, 363 static_cast<const GLvoid*>(plane)); 364 } else { 365 // Boo! Since GLES2 doesn't have GL_UNPACK_ROW_LENGTH and Android doesn't 366 // have GL_EXT_unpack_subimage we have to upload a row at a time. Ick. 367 for (int row = 0; row < height; ++row) { 368 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, row, width, 1, GL_LUMINANCE, 369 GL_UNSIGNED_BYTE, 370 static_cast<const GLvoid*>(plane + (row * stride))); 371 } 372 } 373 } 374 375 void VideoRenderOpenGles20::UpdateTextures(const VideoFrame& frameToRender) { 376 const GLsizei width = frameToRender.width(); 377 const GLsizei height = frameToRender.height(); 378 379 glActiveTexture(GL_TEXTURE0); 380 glBindTexture(GL_TEXTURE_2D, _textureIds[0]); 381 GlTexSubImage2D(width, height, frameToRender.stride(kYPlane), 382 frameToRender.buffer(kYPlane)); 383 384 glActiveTexture(GL_TEXTURE1); 385 glBindTexture(GL_TEXTURE_2D, _textureIds[1]); 386 GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kUPlane), 387 frameToRender.buffer(kUPlane)); 388 389 glActiveTexture(GL_TEXTURE2); 390 glBindTexture(GL_TEXTURE_2D, _textureIds[2]); 391 GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kVPlane), 392 frameToRender.buffer(kVPlane)); 393 394 checkGlError("UpdateTextures"); 395 } 396 397 } // namespace webrtc 398