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/interface/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
VideoRenderOpenGles20(int32_t id)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
~VideoRenderOpenGles20()88 VideoRenderOpenGles20::~VideoRenderOpenGles20() {
89 }
90
Setup(int32_t width,int32_t height)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.
SetCoordinates(int32_t zOrder,const float left,const float top,const float right,const float bottom)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
Render(const I420VideoFrame & frameToRender)217 int32_t VideoRenderOpenGles20::Render(const I420VideoFrame& frameToRender) {
218
219 if (frameToRender.IsZeroSize()) {
220 return -1;
221 }
222
223 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s: id %d",
224 __FUNCTION__, (int) _id);
225
226 glUseProgram(_program);
227 checkGlError("glUseProgram");
228
229 if (_textureWidth != (GLsizei) frameToRender.width() ||
230 _textureHeight != (GLsizei) frameToRender.height()) {
231 SetupTextures(frameToRender);
232 }
233 UpdateTextures(frameToRender);
234
235 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, g_indices);
236 checkGlError("glDrawArrays");
237
238 return 0;
239 }
240
loadShader(GLenum shaderType,const char * pSource)241 GLuint VideoRenderOpenGles20::loadShader(GLenum shaderType,
242 const char* pSource) {
243 GLuint shader = glCreateShader(shaderType);
244 if (shader) {
245 glShaderSource(shader, 1, &pSource, NULL);
246 glCompileShader(shader);
247 GLint compiled = 0;
248 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
249 if (!compiled) {
250 GLint infoLen = 0;
251 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
252 if (infoLen) {
253 char* buf = (char*) malloc(infoLen);
254 if (buf) {
255 glGetShaderInfoLog(shader, infoLen, NULL, buf);
256 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
257 "%s: Could not compile shader %d: %s",
258 __FUNCTION__, shaderType, buf);
259 free(buf);
260 }
261 glDeleteShader(shader);
262 shader = 0;
263 }
264 }
265 }
266 return shader;
267 }
268
createProgram(const char * pVertexSource,const char * pFragmentSource)269 GLuint VideoRenderOpenGles20::createProgram(const char* pVertexSource,
270 const char* pFragmentSource) {
271 GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
272 if (!vertexShader) {
273 return 0;
274 }
275
276 GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
277 if (!pixelShader) {
278 return 0;
279 }
280
281 GLuint program = glCreateProgram();
282 if (program) {
283 glAttachShader(program, vertexShader);
284 checkGlError("glAttachShader");
285 glAttachShader(program, pixelShader);
286 checkGlError("glAttachShader");
287 glLinkProgram(program);
288 GLint linkStatus = GL_FALSE;
289 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
290 if (linkStatus != GL_TRUE) {
291 GLint bufLength = 0;
292 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
293 if (bufLength) {
294 char* buf = (char*) malloc(bufLength);
295 if (buf) {
296 glGetProgramInfoLog(program, bufLength, NULL, buf);
297 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
298 "%s: Could not link program: %s",
299 __FUNCTION__, buf);
300 free(buf);
301 }
302 }
303 glDeleteProgram(program);
304 program = 0;
305 }
306 }
307 return program;
308 }
309
printGLString(const char * name,GLenum s)310 void VideoRenderOpenGles20::printGLString(const char *name, GLenum s) {
311 const char *v = (const char *) glGetString(s);
312 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "GL %s = %s\n",
313 name, v);
314 }
315
checkGlError(const char * op)316 void VideoRenderOpenGles20::checkGlError(const char* op) {
317 #ifdef ANDROID_LOG
318 for (GLint error = glGetError(); error; error = glGetError()) {
319 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
320 "after %s() glError (0x%x)\n", op, error);
321 }
322 #else
323 return;
324 #endif
325 }
326
InitializeTexture(int name,int id,int width,int height)327 static void InitializeTexture(int name, int id, int width, int height) {
328 glActiveTexture(name);
329 glBindTexture(GL_TEXTURE_2D, id);
330 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
331 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
332 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
333 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
334 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0,
335 GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
336 }
337
SetupTextures(const I420VideoFrame & frameToRender)338 void VideoRenderOpenGles20::SetupTextures(const I420VideoFrame& frameToRender) {
339 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id,
340 "%s: width %d, height %d", __FUNCTION__,
341 frameToRender.width(), frameToRender.height());
342
343 const GLsizei width = frameToRender.width();
344 const GLsizei height = frameToRender.height();
345
346 glGenTextures(3, _textureIds); //Generate the Y, U and V texture
347 InitializeTexture(GL_TEXTURE0, _textureIds[0], width, height);
348 InitializeTexture(GL_TEXTURE1, _textureIds[1], width / 2, height / 2);
349 InitializeTexture(GL_TEXTURE2, _textureIds[2], width / 2, height / 2);
350
351 checkGlError("SetupTextures");
352
353 _textureWidth = width;
354 _textureHeight = height;
355 }
356
357 // Uploads a plane of pixel data, accounting for stride != width*bpp.
GlTexSubImage2D(GLsizei width,GLsizei height,int stride,const uint8_t * plane)358 static void GlTexSubImage2D(GLsizei width, GLsizei height, int stride,
359 const uint8_t* plane) {
360 if (stride == width) {
361 // Yay! We can upload the entire plane in a single GL call.
362 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE,
363 GL_UNSIGNED_BYTE,
364 static_cast<const GLvoid*>(plane));
365 } else {
366 // Boo! Since GLES2 doesn't have GL_UNPACK_ROW_LENGTH and Android doesn't
367 // have GL_EXT_unpack_subimage we have to upload a row at a time. Ick.
368 for (int row = 0; row < height; ++row) {
369 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, row, width, 1, GL_LUMINANCE,
370 GL_UNSIGNED_BYTE,
371 static_cast<const GLvoid*>(plane + (row * stride)));
372 }
373 }
374 }
375
UpdateTextures(const I420VideoFrame & frameToRender)376 void VideoRenderOpenGles20::UpdateTextures(const
377 I420VideoFrame& frameToRender) {
378 const GLsizei width = frameToRender.width();
379 const GLsizei height = frameToRender.height();
380
381 glActiveTexture(GL_TEXTURE0);
382 glBindTexture(GL_TEXTURE_2D, _textureIds[0]);
383 GlTexSubImage2D(width, height, frameToRender.stride(kYPlane),
384 frameToRender.buffer(kYPlane));
385
386 glActiveTexture(GL_TEXTURE1);
387 glBindTexture(GL_TEXTURE_2D, _textureIds[1]);
388 GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kUPlane),
389 frameToRender.buffer(kUPlane));
390
391 glActiveTexture(GL_TEXTURE2);
392 glBindTexture(GL_TEXTURE_2D, _textureIds[2]);
393 GlTexSubImage2D(width / 2, height / 2, frameToRender.stride(kVPlane),
394 frameToRender.buffer(kVPlane));
395
396 checkGlError("UpdateTextures");
397 }
398
399 } // namespace webrtc
400