3 * Copyright 2014, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 import java.nio.ByteBuffer;
31 import java.nio.ByteOrder;
32 import java.nio.FloatBuffer;
33 import java.util.ArrayList;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.LinkedBlockingQueue;
37 import javax.microedition.khronos.egl.EGLConfig;
38 import javax.microedition.khronos.opengles.GL10;
40 import android.graphics.SurfaceTexture;
41 import android.opengl.EGL14;
42 import android.opengl.EGLContext;
43 import android.opengl.GLES11Ext;
44 import android.opengl.GLES20;
45 import android.opengl.GLSurfaceView;
46 import android.util.Log;
48 import org.webrtc.VideoRenderer.I420Frame;
51 * Efficiently renders YUV frames using the GPU for CSC.
52 * Clients will want first to call setView() to pass GLSurfaceView
53 * and then for each video stream either create instance of VideoRenderer using
54 * createGui() call or VideoRenderer.Callbacks interface using create() call.
55 * Only one instance of the class can be created.
57 public class VideoRendererGui implements GLSurfaceView.Renderer {
58 private static VideoRendererGui instance = null;
59 private static final String TAG = "VideoRendererGui";
60 private GLSurfaceView surface;
61 private static EGLContext eglContext = null;
62 // Indicates if SurfaceView.Renderer.onSurfaceCreated was called.
63 // If true then for every newly created yuv image renderer createTexture()
64 // should be called. The variable is accessed on multiple threads and
65 // all accesses are synchronized on yuvImageRenderers' object lock.
66 private boolean onSurfaceCreatedCalled;
67 private int screenWidth;
68 private int screenHeight;
69 // List of yuv renderers.
70 private ArrayList<YuvImageRenderer> yuvImageRenderers;
71 private int yuvProgram;
72 private int oesProgram;
73 // Types of video scaling:
74 // SCALE_ASPECT_FIT - video frame is scaled to fit the size of the view by
75 // maintaining the aspect ratio (black borders may be displayed).
76 // SCALE_ASPECT_FILL - video frame is scaled to fill the size of the view by
77 // maintaining the aspect ratio. Some portion of the video frame may be
79 // SCALE_FILL - video frame is scaled to to fill the size of the view. Video
80 // aspect ratio is changed if necessary.
81 private static enum ScalingType
82 { SCALE_ASPECT_FIT, SCALE_ASPECT_FILL, SCALE_FILL };
84 private final String VERTEX_SHADER_STRING =
85 "varying vec2 interp_tc;\n" +
86 "attribute vec4 in_pos;\n" +
87 "attribute vec2 in_tc;\n" +
90 " gl_Position = in_pos;\n" +
91 " interp_tc = in_tc;\n" +
94 private final String YUV_FRAGMENT_SHADER_STRING =
95 "precision mediump float;\n" +
96 "varying vec2 interp_tc;\n" +
98 "uniform sampler2D y_tex;\n" +
99 "uniform sampler2D u_tex;\n" +
100 "uniform sampler2D v_tex;\n" +
103 // CSC according to http://www.fourcc.org/fccyvrgb.php
104 " float y = texture2D(y_tex, interp_tc).r;\n" +
105 " float u = texture2D(u_tex, interp_tc).r - 0.5;\n" +
106 " float v = texture2D(v_tex, interp_tc).r - 0.5;\n" +
107 " gl_FragColor = vec4(y + 1.403 * v, " +
108 " y - 0.344 * u - 0.714 * v, " +
109 " y + 1.77 * u, 1);\n" +
113 private static final String OES_FRAGMENT_SHADER_STRING =
114 "#extension GL_OES_EGL_image_external : require\n" +
115 "precision mediump float;\n" +
116 "varying vec2 interp_tc;\n" +
118 "uniform samplerExternalOES oes_tex;\n" +
121 " gl_FragColor = texture2D(oes_tex, interp_tc);\n" +
125 private VideoRendererGui(GLSurfaceView surface) {
126 this.surface = surface;
127 // Create an OpenGL ES 2.0 context.
128 surface.setPreserveEGLContextOnPause(true);
129 surface.setEGLContextClientVersion(2);
130 surface.setRenderer(this);
131 surface.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
133 yuvImageRenderers = new ArrayList<YuvImageRenderer>();
136 // Poor-man's assert(): die with |msg| unless |condition| is true.
137 private static void abortUnless(boolean condition, String msg) {
139 throw new RuntimeException(msg);
143 // Assert that no OpenGL ES 2.0 error has been raised.
144 private static void checkNoGLES2Error() {
145 int error = GLES20.glGetError();
146 abortUnless(error == GLES20.GL_NO_ERROR, "GLES20 error: " + error);
149 // Wrap a float[] in a direct FloatBuffer using native byte order.
150 private static FloatBuffer directNativeFloatBuffer(float[] array) {
151 FloatBuffer buffer = ByteBuffer.allocateDirect(array.length * 4).order(
152 ByteOrder.nativeOrder()).asFloatBuffer();
158 private int loadShader(int shaderType, String source) {
159 int[] result = new int[] {
162 int shader = GLES20.glCreateShader(shaderType);
163 GLES20.glShaderSource(shader, source);
164 GLES20.glCompileShader(shader);
165 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, 0);
166 if (result[0] != GLES20.GL_TRUE) {
167 Log.e(TAG, "Could not compile shader " + shaderType + ":" +
168 GLES20.glGetShaderInfoLog(shader));
169 throw new RuntimeException(GLES20.glGetShaderInfoLog(shader));
176 private int createProgram(String vertexSource, String fragmentSource) {
177 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
178 int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
179 int program = GLES20.glCreateProgram();
181 throw new RuntimeException("Could not create program");
183 GLES20.glAttachShader(program, vertexShader);
184 GLES20.glAttachShader(program, fragmentShader);
185 GLES20.glLinkProgram(program);
186 int[] linkStatus = new int[] {
189 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
190 if (linkStatus[0] != GLES20.GL_TRUE) {
191 Log.e(TAG, "Could not link program: " +
192 GLES20.glGetProgramInfoLog(program));
193 throw new RuntimeException(GLES20.glGetProgramInfoLog(program));
200 * Class used to display stream of YUV420 frames at particular location
201 * on a screen. New video frames are sent to display using renderFrame()
204 private static class YuvImageRenderer implements VideoRenderer.Callbacks {
205 private GLSurfaceView surface;
207 private int yuvProgram;
208 private int oesProgram;
209 private int[] yuvTextures = { -1, -1, -1 };
210 private int oesTexture = -1;
211 private float[] stMatrix = new float[16];
213 // Render frame queue - accessed by two threads. renderFrame() call does
214 // an offer (writing I420Frame to render) and early-returns (recording
215 // a dropped frame) if that queue is full. draw() call does a peek(),
216 // copies frame to texture and then removes it from a queue using poll().
217 LinkedBlockingQueue<I420Frame> frameToRenderQueue;
218 // Local copy of incoming video frame.
219 private I420Frame yuvFrameToRender;
220 private I420Frame textureFrameToRender;
221 // Type of video frame used for recent frame rendering.
222 private static enum RendererType { RENDERER_YUV, RENDERER_TEXTURE };
223 private RendererType rendererType;
224 private ScalingType scalingType;
225 // Flag if renderFrame() was ever called.
227 // Total number of video frames received in renderFrame() call.
228 private int framesReceived;
229 // Number of video frames dropped by renderFrame() because previous
230 // frame has not been rendered yet.
231 private int framesDropped;
232 // Number of rendered video frames.
233 private int framesRendered;
234 // Time in ns when the first video frame was rendered.
235 private long startTimeNs = -1;
236 // Time in ns spent in draw() function.
237 private long drawTimeNs;
238 // Time in ns spent in renderFrame() function - including copying frame
239 // data to rendering planes.
240 private long copyTimeNs;
242 private float texLeft;
243 private float texRight;
244 private float texTop;
245 private float texBottom;
246 private FloatBuffer textureVertices;
247 // Texture UV coordinates offsets.
248 private float texOffsetU;
249 private float texOffsetV;
250 private FloatBuffer textureCoords;
251 // Flag if texture vertices or coordinates update is needed.
252 private boolean updateTextureProperties;
253 // Viewport dimensions.
254 private int screenWidth;
255 private int screenHeight;
257 private int videoWidth;
258 private int videoHeight;
260 private YuvImageRenderer(
261 GLSurfaceView surface, int id,
262 int x, int y, int width, int height,
263 ScalingType scalingType) {
264 Log.d(TAG, "YuvImageRenderer.Create id: " + id);
265 this.surface = surface;
267 this.scalingType = scalingType;
268 frameToRenderQueue = new LinkedBlockingQueue<I420Frame>(1);
269 // Create texture vertices.
270 texLeft = (x - 50) / 50.0f;
271 texTop = (50 - y) / 50.0f;
272 texRight = Math.min(1.0f, (x + width - 50) / 50.0f);
273 texBottom = Math.max(-1.0f, (50 - y - height) / 50.0f);
274 float textureVeticesFloat[] = new float[] {
280 textureVertices = directNativeFloatBuffer(textureVeticesFloat);
281 // Create texture UV coordinates.
284 float textureCoordinatesFloat[] = new float[] {
285 texOffsetU, texOffsetV, // left top
286 texOffsetU, 1.0f - texOffsetV, // left bottom
287 1.0f - texOffsetU, texOffsetV, // right top
288 1.0f - texOffsetU, 1.0f - texOffsetV // right bottom
290 textureCoords = directNativeFloatBuffer(textureCoordinatesFloat);
291 updateTextureProperties = false;
294 private void createTextures(int yuvProgram, int oesProgram) {
295 Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" +
296 Thread.currentThread().getId());
297 this.yuvProgram = yuvProgram;
298 this.oesProgram = oesProgram;
300 // Generate 3 texture ids for Y/U/V and place them into |yuvTextures|.
301 GLES20.glGenTextures(3, yuvTextures, 0);
302 for (int i = 0; i < 3; i++) {
303 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
304 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
305 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
306 128, 128, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, null);
307 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
308 GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
309 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
310 GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
311 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
312 GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
313 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
314 GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
319 private void checkAdjustTextureCoords() {
320 if (!updateTextureProperties ||
321 scalingType == ScalingType.SCALE_FILL) {
324 // Re - calculate texture vertices to preserve video aspect ratio.
325 float texRight = this.texRight;
326 float texLeft = this.texLeft;
327 float texTop = this.texTop;
328 float texBottom = this.texBottom;
329 float displayWidth = (texRight - texLeft) * screenWidth / 2;
330 float displayHeight = (texTop - texBottom) * screenHeight / 2;
331 if (displayWidth > 1 && displayHeight > 1 &&
332 videoWidth > 1 && videoHeight > 1) {
333 float displayAspectRatio = displayWidth / displayHeight;
334 float videoAspectRatio = (float)videoWidth / videoHeight;
335 if (scalingType == ScalingType.SCALE_ASPECT_FIT) {
336 // Need to re-adjust vertices width or height to match video AR.
337 if (displayAspectRatio > videoAspectRatio) {
338 float deltaX = (displayWidth - videoAspectRatio * displayHeight) /
339 instance.screenWidth;
343 float deltaY = (displayHeight - displayWidth / videoAspectRatio) /
344 instance.screenHeight;
348 // Re-allocate vertices buffer to adjust to video aspect ratio.
349 float textureVeticesFloat[] = new float[] {
355 textureVertices = directNativeFloatBuffer(textureVeticesFloat);
357 if (scalingType == ScalingType.SCALE_ASPECT_FILL) {
358 // Need to re-adjust UV coordinates to match display AR.
359 if (displayAspectRatio > videoAspectRatio) {
360 texOffsetV = (1.0f - videoAspectRatio / displayAspectRatio) / 2.0f;
362 texOffsetU = (1.0f - displayAspectRatio / videoAspectRatio) / 2.0f;
364 // Re-allocate coordinates buffer to adjust to display aspect ratio.
365 float textureCoordinatesFloat[] = new float[] {
366 texOffsetU, texOffsetV, // left top
367 texOffsetU, 1.0f - texOffsetV, // left bottom
368 1.0f - texOffsetU, texOffsetV, // right top
369 1.0f - texOffsetU, 1.0f - texOffsetV // right bottom
371 textureCoords = directNativeFloatBuffer(textureCoordinatesFloat);
374 updateTextureProperties = false;
377 private void draw() {
379 // No frame received yet - nothing to render.
382 // Check if texture vertices/coordinates adjustment is required when
383 // screen orientation changes or video frame size changes.
384 checkAdjustTextureCoords();
386 long now = System.nanoTime();
388 I420Frame frameFromQueue;
389 synchronized (frameToRenderQueue) {
390 frameFromQueue = frameToRenderQueue.peek();
391 if (frameFromQueue != null && startTimeNs == -1) {
395 if (rendererType == RendererType.RENDERER_YUV) {
396 // YUV textures rendering.
397 GLES20.glUseProgram(yuvProgram);
399 for (int i = 0; i < 3; ++i) {
400 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
401 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
402 if (frameFromQueue != null) {
404 frameFromQueue.width : frameFromQueue.width / 2;
406 frameFromQueue.height : frameFromQueue.height / 2;
407 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
408 w, h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
409 frameFromQueue.yuvPlanes[i]);
413 // External texture rendering.
414 GLES20.glUseProgram(oesProgram);
416 if (frameFromQueue != null) {
417 oesTexture = frameFromQueue.textureId;
418 if (frameFromQueue.textureObject instanceof SurfaceTexture) {
419 SurfaceTexture surfaceTexture =
420 (SurfaceTexture) frameFromQueue.textureObject;
421 surfaceTexture.updateTexImage();
422 surfaceTexture.getTransformMatrix(stMatrix);
425 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
426 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTexture);
429 if (frameFromQueue != null) {
430 frameToRenderQueue.poll();
434 if (rendererType == RendererType.RENDERER_YUV) {
435 GLES20.glUniform1i(GLES20.glGetUniformLocation(yuvProgram, "y_tex"), 0);
436 GLES20.glUniform1i(GLES20.glGetUniformLocation(yuvProgram, "u_tex"), 1);
437 GLES20.glUniform1i(GLES20.glGetUniformLocation(yuvProgram, "v_tex"), 2);
440 int posLocation = GLES20.glGetAttribLocation(yuvProgram, "in_pos");
441 if (posLocation == -1) {
442 throw new RuntimeException("Could not get attrib location for in_pos");
444 GLES20.glEnableVertexAttribArray(posLocation);
445 GLES20.glVertexAttribPointer(
446 posLocation, 2, GLES20.GL_FLOAT, false, 0, textureVertices);
448 int texLocation = GLES20.glGetAttribLocation(yuvProgram, "in_tc");
449 if (texLocation == -1) {
450 throw new RuntimeException("Could not get attrib location for in_tc");
452 GLES20.glEnableVertexAttribArray(texLocation);
453 GLES20.glVertexAttribPointer(
454 texLocation, 2, GLES20.GL_FLOAT, false, 0, textureCoords);
456 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
458 GLES20.glDisableVertexAttribArray(posLocation);
459 GLES20.glDisableVertexAttribArray(texLocation);
463 if (frameFromQueue != null) {
465 drawTimeNs += (System.nanoTime() - now);
466 if ((framesRendered % 150) == 0) {
472 private void logStatistics() {
473 long timeSinceFirstFrameNs = System.nanoTime() - startTimeNs;
474 Log.d(TAG, "ID: " + id + ". Type: " + rendererType +
475 ". Frames received: " + framesReceived +
476 ". Dropped: " + framesDropped + ". Rendered: " + framesRendered);
477 if (framesReceived > 0 && framesRendered > 0) {
478 Log.d(TAG, "Duration: " + (int)(timeSinceFirstFrameNs / 1e6) +
479 " ms. FPS: " + (float)framesRendered * 1e9 / timeSinceFirstFrameNs);
480 Log.d(TAG, "Draw time: " +
481 (int) (drawTimeNs / (1000 * framesRendered)) + " us. Copy time: " +
482 (int) (copyTimeNs / (1000 * framesReceived)) + " us");
486 public void setScreenSize(final int screenWidth, final int screenHeight) {
487 this.screenWidth = screenWidth;
488 this.screenHeight = screenHeight;
489 updateTextureProperties = true;
493 public void setSize(final int width, final int height) {
494 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setSize: " +
495 width + " x " + height);
497 videoHeight = height;
498 int[] strides = { width, width / 2, width / 2 };
499 // Frame re-allocation need to be synchronized with copying
500 // frame to textures in draw() function to avoid re-allocating
501 // the frame while it is being copied.
502 synchronized (frameToRenderQueue) {
503 // Clear rendering queue.
504 frameToRenderQueue.poll();
505 // Re-allocate / allocate the frame.
506 yuvFrameToRender = new I420Frame(width, height, strides, null);
507 textureFrameToRender = new I420Frame(width, height, null, -1);
508 updateTextureProperties = true;
513 public synchronized void renderFrame(I420Frame frame) {
514 long now = System.nanoTime();
516 // Skip rendering of this frame if setSize() was not called.
517 if (yuvFrameToRender == null || textureFrameToRender == null) {
521 // Check input frame parameters.
522 if (frame.yuvFrame) {
523 if (!(frame.yuvStrides[0] == frame.width &&
524 frame.yuvStrides[1] == frame.width / 2 &&
525 frame.yuvStrides[2] == frame.width / 2)) {
526 Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " +
527 frame.yuvStrides[1] + ", " + frame.yuvStrides[2]);
530 // Check incoming frame dimensions.
531 if (frame.width != yuvFrameToRender.width ||
532 frame.height != yuvFrameToRender.height) {
533 throw new RuntimeException("Wrong frame size " +
534 frame.width + " x " + frame.height);
538 if (frameToRenderQueue.size() > 0) {
539 // Skip rendering of this frame if previous frame was not rendered yet.
544 // Create a local copy of the frame.
545 if (frame.yuvFrame) {
546 yuvFrameToRender.copyFrom(frame);
547 rendererType = RendererType.RENDERER_YUV;
548 frameToRenderQueue.offer(yuvFrameToRender);
550 textureFrameToRender.copyFrom(frame);
551 rendererType = RendererType.RENDERER_TEXTURE;
552 frameToRenderQueue.offer(textureFrameToRender);
554 copyTimeNs += (System.nanoTime() - now);
557 // Request rendering.
558 surface.requestRender();
563 /** Passes GLSurfaceView to video renderer. */
564 public static void setView(GLSurfaceView surface) {
565 Log.d(TAG, "VideoRendererGui.setView");
566 instance = new VideoRendererGui(surface);
569 public static EGLContext getEGLContext() {
574 * Creates VideoRenderer with top left corner at (x, y) and resolution
575 * (width, height). All parameters are in percentage of screen resolution.
577 public static VideoRenderer createGui(
578 int x, int y, int width, int height) throws Exception {
579 YuvImageRenderer javaGuiRenderer = create(x, y, width, height);
580 return new VideoRenderer(javaGuiRenderer);
583 public static VideoRenderer.Callbacks createGuiRenderer(
584 int x, int y, int width, int height) {
585 return create(x, y, width, height);
589 * Creates VideoRenderer.Callbacks with top left corner at (x, y) and
590 * resolution (width, height). All parameters are in percentage of
593 public static YuvImageRenderer create(
594 int x, int y, int width, int height) {
595 // Check display region parameters.
596 if (x < 0 || x > 100 || y < 0 || y > 100 ||
597 width < 0 || width > 100 || height < 0 || height > 100 ||
598 x + width > 100 || y + height > 100) {
599 throw new RuntimeException("Incorrect window parameters.");
602 if (instance == null) {
603 throw new RuntimeException(
604 "Attempt to create yuv renderer before setting GLSurfaceView");
606 final YuvImageRenderer yuvImageRenderer = new YuvImageRenderer(
607 instance.surface, instance.yuvImageRenderers.size(),
608 x, y, width, height, ScalingType.SCALE_ASPECT_FIT);
609 synchronized (instance.yuvImageRenderers) {
610 if (instance.onSurfaceCreatedCalled) {
611 // onSurfaceCreated has already been called for VideoRendererGui -
612 // need to create texture for new image and add image to the
614 final CountDownLatch countDownLatch = new CountDownLatch(1);
615 instance.surface.queueEvent(new Runnable() {
617 yuvImageRenderer.createTextures(
618 instance.yuvProgram, instance.oesProgram);
619 yuvImageRenderer.setScreenSize(
620 instance.screenWidth, instance.screenHeight);
621 countDownLatch.countDown();
624 // Wait for task completion.
626 countDownLatch.await();
627 } catch (InterruptedException e) {
628 throw new RuntimeException(e);
631 // Add yuv renderer to rendering list.
632 instance.yuvImageRenderers.add(yuvImageRenderer);
634 return yuvImageRenderer;
638 public void onSurfaceCreated(GL10 unused, EGLConfig config) {
639 Log.d(TAG, "VideoRendererGui.onSurfaceCreated");
640 // Store render EGL context
641 eglContext = EGL14.eglGetCurrentContext();
642 Log.d(TAG, "VideoRendererGui EGL Context: " + eglContext);
644 // Create YUV and OES programs.
645 yuvProgram = createProgram(VERTEX_SHADER_STRING,
646 YUV_FRAGMENT_SHADER_STRING);
647 oesProgram = createProgram(VERTEX_SHADER_STRING,
648 OES_FRAGMENT_SHADER_STRING);
650 synchronized (yuvImageRenderers) {
651 // Create textures for all images.
652 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) {
653 yuvImageRenderer.createTextures(yuvProgram, oesProgram);
655 onSurfaceCreatedCalled = true;
658 GLES20.glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
662 public void onSurfaceChanged(GL10 unused, int width, int height) {
663 Log.d(TAG, "VideoRendererGui.onSurfaceChanged: " +
664 width + " x " + height + " ");
666 screenHeight = height;
667 GLES20.glViewport(0, 0, width, height);
668 synchronized (yuvImageRenderers) {
669 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) {
670 yuvImageRenderer.setScreenSize(screenWidth, screenHeight);
676 public void onDrawFrame(GL10 unused) {
677 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
678 synchronized (yuvImageRenderers) {
679 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) {
680 yuvImageRenderer.draw();