Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(70)

Side by Side Diff: talk/app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java

Issue 1460703002: Implement AndroidTextureBuffer::NativeToI420. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Addressed Magnus' comments. Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * libjingle 2 * libjingle
3 * Copyright 2015 Google Inc. 3 * Copyright 2015 Google Inc.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright notice, 8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer. 9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice, 10 * 2. Redistributions in binary form must reproduce the above copyright notice,
(...skipping 17 matching lines...) Expand all
28 package org.webrtc; 28 package org.webrtc;
29 29
30 import android.graphics.SurfaceTexture; 30 import android.graphics.SurfaceTexture;
31 import android.opengl.GLES11Ext; 31 import android.opengl.GLES11Ext;
32 import android.opengl.GLES20; 32 import android.opengl.GLES20;
33 import android.os.Build; 33 import android.os.Build;
34 import android.os.Handler; 34 import android.os.Handler;
35 import android.os.HandlerThread; 35 import android.os.HandlerThread;
36 import android.os.SystemClock; 36 import android.os.SystemClock;
37 37
38 import org.webrtc.Logging;
39
40 import java.nio.ByteBuffer;
41 import java.nio.FloatBuffer;
38 import java.util.concurrent.Callable; 42 import java.util.concurrent.Callable;
39 import java.util.concurrent.CountDownLatch; 43 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit; 44 import java.util.concurrent.TimeUnit;
41 45
42 import javax.microedition.khronos.egl.EGLContext; 46 import javax.microedition.khronos.egl.EGLContext;
43 47
44 /** 48 /**
45 * Helper class to create and synchronize access to a SurfaceTexture. The caller will get notified 49 * Helper class to create and synchronize access to a SurfaceTexture. The caller will get notified
46 * of new frames in onTextureFrameAvailable(), and should call returnTextureFram e() when done with 50 * of new frames in onTextureFrameAvailable(), and should call returnTextureFram e() when done with
47 * the frame. Only one texture frame can be in flight at once, so returnTextureF rame() must be 51 * the frame. Only one texture frame can be in flight at once, so returnTextureF rame() must be
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.andr oid/android/5.1.1_r1/android/graphics/SurfaceTexture.java#195. 91 // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.andr oid/android/5.1.1_r1/android/graphics/SurfaceTexture.java#195.
88 // Therefore, in order to control the callback thread on API lvl < 21, the S urfaceTextureHelper 92 // Therefore, in order to control the callback thread on API lvl < 21, the S urfaceTextureHelper
89 // is constructed on the |handler| thread. 93 // is constructed on the |handler| thread.
90 return ThreadUtils.invokeUninterruptibly(finalHandler, new Callable<SurfaceT extureHelper>() { 94 return ThreadUtils.invokeUninterruptibly(finalHandler, new Callable<SurfaceT extureHelper>() {
91 @Override public SurfaceTextureHelper call() { 95 @Override public SurfaceTextureHelper call() {
92 return new SurfaceTextureHelper(sharedContext, finalHandler, (handler == null)); 96 return new SurfaceTextureHelper(sharedContext, finalHandler, (handler == null));
93 } 97 }
94 }); 98 });
95 } 99 }
96 100
101 // State for YUV conversion, instantiated on demand.
102 static private class ConvertYUV {
perkj_webrtc 2015/12/07 12:53:50 rename YuvConverter?
nisse-webrtc 2015/12/08 11:44:51 Done.
103 private final EglBase eglBase;
104 private final GlShader shader;
105 private boolean released = false;
106
107 // Vertex coordinates in Normalized Device Coordinates, i.e.
108 // (-1, -1) is bottom-left and (1, 1) is top-right.
109 private static final FloatBuffer DEVICE_RECTANGLE =
110 GlUtil.createFloatBuffer(new float[] {
111 -1.0f, -1.0f, // Bottom left.
112 1.0f, -1.0f, // Bottom right.
113 -1.0f, 1.0f, // Top left.
114 1.0f, 1.0f, // Top right.
115 });
116
117 // Texture coordinates - (0, 0) is bottom-left and (1, 1) is top-right.
118 private static final FloatBuffer TEXTURE_RECTANGLE =
119 GlUtil.createFloatBuffer(new float[] {
120 0.0f, 0.0f, // Bottom left.
121 1.0f, 0.0f, // Bottom right.
122 0.0f, 1.0f, // Top left.
123 1.0f, 1.0f // Top right.
124 });
125
126 private static final String VERTEX_SHADER =
127 "varying vec2 interp_tc;\n"
128 + "attribute vec4 in_pos;\n"
129 + "attribute vec4 in_tc;\n"
130 + "\n"
131 + "uniform mat4 texMatrix;\n"
132 + "\n"
133 + "void main() {\n"
134 + " gl_Position = in_pos;\n"
135 + " interp_tc = (texMatrix * in_tc).xy;\n"
136 + "}\n";
137
138 private static final String FRAGMENT_SHADER =
139 "#extension GL_OES_EGL_image_external : require\n"
140 + "precision mediump float;\n"
141 + "varying vec2 interp_tc;\n"
142 + "\n"
143 + "uniform samplerExternalOES oesTex;\n"
144 // Difference in texture coordinate corresponding to one
145 // sub-pixel in the x direction.
146 + "uniform vec2 xUnit;\n"
147 // Color conversion coefficients, including constant term
148 + "uniform vec4 coeffs;\n"
149 + "\n"
150 + "void main() {\n"
151 // This more elegant expression gives a worse framerate.
152 // Possibly because the additional multiplies by 1.0.
153 // + " gl_FragColor = coeffs * mat4(\n"
perkj_webrtc 2015/12/07 12:53:50 remove code you don't use. If you want, you can wr
nisse-webrtc 2015/12/08 11:44:51 Done.
154 // + " texture2D(oesTex, interp_tc - 1.5 * xUnit),\n"
155 // + " texture2D(oesTex, interp_tc - 0.5 * xUnit),\n"
156 // + " texture2D(oesTex, interp_tc + 0.5 * xUnit),\n"
157 // + " texture2D(oesTex, interp_tc + 1.5 * xUnit));\n"
158
159 // And this variant, avoiding those multiplications, fail to compile
160 // + " gl_FragColor = vec4(coeffs.a) + coeffs.rgb * mat3x4(\n"
161 // + " texture2D(oesTex, interp_tc - 1.5 * xUnit).rgb,\n"
162 // + " texture2D(oesTex, interp_tc - 0.5 * xUnit).rgb,\n"
163 // + " texture2D(oesTex, interp_tc + 0.5 * xUnit).rgb,\n"
164 // + " texture2D(oesTex, interp_tc + 1.5 * xUnit).rgb);\n"
165
166 + " gl_FragColor.r = coeffs.a + dot(coeffs.rgb,\n"
167 + " texture2D(oesTex, interp_tc - 1.5 * xUnit).rgb);\n"
168 + " gl_FragColor.g = coeffs.a + dot(coeffs.rgb,\n"
169 + " texture2D(oesTex, interp_tc - 0.5 * xUnit).rgb);\n"
170 + " gl_FragColor.b = coeffs.a + dot(coeffs.rgb,\n"
171 + " texture2D(oesTex, interp_tc + 0.5 * xUnit).rgb);\n"
172 + " gl_FragColor.a = coeffs.a + dot(coeffs.rgb,\n"
173 + " texture2D(oesTex, interp_tc + 1.5 * xUnit).rgb);\n"
174 + "}\n";
175
176 private int texMatrixLoc;
177 private int xUnitLoc;
178 private int coeffsLoc;;
179
180 ConvertYUV (EGLContext sharedContext) {
181 eglBase = new EglBase(sharedContext, EglBase.CONFIG_PIXEL_RGBA_BUFFER);
182 eglBase.createDummyPbufferSurface();
183 eglBase.makeCurrent();
184
185 shader = new GlShader(VERTEX_SHADER, FRAGMENT_SHADER);
186 shader.useProgram();
187 texMatrixLoc = shader.getUniformLocation("texMatrix");
188 xUnitLoc = shader.getUniformLocation("xUnit");
189 coeffsLoc = shader.getUniformLocation("coeffs");
190 GLES20.glUniform1i(shader.getUniformLocation("oesTex"), 0);
191 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values.");
192 // Initialize vertex shader attributes.
193 shader.setVertexAttribArray("in_pos", 2, DEVICE_RECTANGLE);
194 // If the width is not a multiple of 4 pixels, the texture
195 // will be scaled up slightly and clipped at the right border.
196 shader.setVertexAttribArray("in_tc", 2, TEXTURE_RECTANGLE);
197 }
198
199 synchronized public void convert(ByteBuffer buf,
perkj_webrtc 2015/12/07 12:53:50 not for use outside webrtc.org. Remove public.
nisse-webrtc 2015/12/08 11:44:51 Done.
200 int width, int height, int stride, int textureId, float [] transformMatr ix) {
201 // TODO(nisse): For now only produces the Y plane.
perkj_webrtc 2015/12/07 12:53:50 Really?
nisse-webrtc 2015/12/08 11:44:51 Removed.
202 if (released) {
203 throw new IllegalStateException(
204 "ConvertYUV.convert called on released object");
205 }
206 if (stride % 8 != 0) {
207 throw new IllegalArgumentException(
208 "Invalid stride, must be a multiple of 8");
209 }
210 if (stride < width){
211 throw new IllegalArgumentException(
212 "Invalid stride, must >= width");
213 }
214
215 int y_width = (width+3) / 4;
216 int uv_width = (width+7) / 8;
217 int uv_height = (height+1)/2;
218 int total_height = height + uv_height;
219 int size = stride * total_height;
220
221 Logging.e(TAG, "width: " + width + ", height: " + height +
perkj_webrtc 2015/12/07 12:53:50 remove per frame logging
222 ", y-width: " + y_width + ", stride: " + stride);
223
224 Logging.e(TAG, "matrix: \n " + transformMatrix[0]
225 + " " + transformMatrix[1]
226 + " " + transformMatrix[2]
227 + " " + transformMatrix[3]
228 + "\n " + transformMatrix[4]
229 + " " + transformMatrix[5]
230 + " " + transformMatrix[6]
231 + " " + transformMatrix[7]
232 + "\n " + transformMatrix[8]
233 + " " + transformMatrix[9]
234 + " " + transformMatrix[10]
235 + " " + transformMatrix[11]
236 + "\n " + transformMatrix[12]
237 + " " + transformMatrix[13]
238 + " " + transformMatrix[14]
239 + " " + transformMatrix[15]);
240
241 if (buf.capacity() < size) {
242 Logging.e(TAG, "needed size: " + size + ", available size: " + buf.capac ity());
243 throw new IllegalArgumentException("ConvertYUV.convert called with too s mall buffer");
244 }
245 // Produce a frame buffer starting at top-left corner, not
246 // bottom-left.
247 transformMatrix =
248 RendererCommon.multiplyMatrices(transformMatrix,
249 RendererCommon.verticalFlipMatrix());
250
251 try {
252 // Reuse surface, if possible. TODO(nisse): Add an eglBase
253 // helper function, say, makeCurrentWithSize.
254 if (eglBase.hasSurface()) {
255 if (eglBase.surfaceWidth() != stride/4 ||
256 eglBase.surfaceHeight() != total_height){
257 eglBase.releaseSurface();
258 eglBase.createPbufferSurface(stride/4, total_height);
259 }
260 } else {
261 eglBase.createPbufferSurface(stride/4, total_height);
262 }
263
264 eglBase.makeCurrent();
265
266 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values.");
267
268 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
269 GlUtil.checkNoGLES2Error("Active texture");
270 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
271 GLES20.glUniformMatrix4fv(texMatrixLoc, 1, false, transformMatrix, 0);
272
273 // Draw Y */
274 GLES20.glViewport(0, 0, y_width, height);
275 GlUtil.checkNoGLES2Error("Viewport");
276 // Matrix * (1;0;0;0) / width. Note that opengl uses column major order.
277 GLES20.glUniform2f(xUnitLoc,
278 transformMatrix[0] / width,
279 transformMatrix[1] / width);
280 // Y'UV444 to RGB888, see
281 // https://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion
282 GLES20.glUniform4f(coeffsLoc, 0.299f, 0.587f, 0.114f, 0.0f);
283 GlUtil.checkNoGLES2Error("Binding texture and matrix");
284
285 Logging.e(TAG, "ConvertYUV: glDrawArrays");
286 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
287 GlUtil.checkNoGLES2Error("glDrawArrays, Y");
288
289 // Draw U */
290 GLES20.glViewport(0, height, uv_width, uv_height);
291 GlUtil.checkNoGLES2Error("Viewport");
292 // Matrix * (1;0;0;0) / (2*width). Note that opengl uses column major or der.
293 GLES20.glUniform2f(xUnitLoc,
294 transformMatrix[0] / (2.0f*width),
295 transformMatrix[1] / (2.0f*width));
296 /* Use ITU-R coefficients for U and V */
297 GLES20.glUniform4f(coeffsLoc, -0.169f, -0.331f, 0.499f, 0.5f);
298 GlUtil.checkNoGLES2Error("Binding texture and matrix");
299
300 Logging.e(TAG, "ConvertYUV: glDrawArrays");
301 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
302 GlUtil.checkNoGLES2Error("glDrawArrays, U");
303
304 // Draw V */
305 GLES20.glViewport(stride/8, height, uv_width, uv_height);
306 GlUtil.checkNoGLES2Error("Viewport");
307 /* Use ITU-R coefficients for U and V */
308 GLES20.glUniform4f(coeffsLoc, 0.499f, -0.418f, -0.0813f, 0.5f);
309 GlUtil.checkNoGLES2Error("Binding texture and matrix");
310
311 Logging.e(TAG, "ConvertYUV: glDrawArrays");
312 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
313 GlUtil.checkNoGLES2Error("glDrawArrays, V");
314
315 Logging.e(TAG, "ConvertYUV: glReadPixels");
316 GLES20.glReadPixels(0, 0, stride/4, total_height, GLES20.GL_RGBA,
317 GLES20.GL_UNSIGNED_BYTE, buf);
318 GlUtil.checkNoGLES2Error("glReadPixels");
319
320 // Unbind texture. Reportedly needed on some devices to get
321 // the texture updated from the camera.
322 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
323 }
324 finally {
325 eglBase.detachCurrent();
326 }
327 }
328
329 synchronized public void release() {
330 released = true;
331 eglBase.makeCurrent();
332 shader.release();
333 eglBase.release();
334 }
335 }
336
97 private final Handler handler; 337 private final Handler handler;
98 private boolean isOwningThread; 338 private boolean isOwningThread;
99 private final EglBase eglBase; 339 private final EglBase eglBase;
100 private final SurfaceTexture surfaceTexture; 340 private final SurfaceTexture surfaceTexture;
101 private final int oesTextureId; 341 private final int oesTextureId;
342 private ConvertYUV convertYUV;
343
102 private OnTextureFrameAvailableListener listener; 344 private OnTextureFrameAvailableListener listener;
103 // The possible states of this class. 345 // The possible states of this class.
104 private boolean hasPendingTexture = false; 346 private boolean hasPendingTexture = false;
105 private boolean isTextureInUse = false; 347 private boolean isTextureInUse = false;
106 private boolean isQuitting = false; 348 private boolean isQuitting = false;
107 349
108 private SurfaceTextureHelper(EGLContext sharedContext, Handler handler, boolea n isOwningThread) { 350 private SurfaceTextureHelper(EGLContext sharedContext, Handler handler, boolea n isOwningThread) {
109 if (handler.getLooper().getThread() != Thread.currentThread()) { 351 if (handler.getLooper().getThread() != Thread.currentThread()) {
110 throw new IllegalStateException("SurfaceTextureHelper must be created on t he handler thread"); 352 throw new IllegalStateException("SurfaceTextureHelper must be created on t he handler thread");
111 } 353 }
112 this.handler = handler; 354 this.handler = handler;
113 this.isOwningThread = isOwningThread; 355 this.isOwningThread = isOwningThread;
114 356
115 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); 357 eglBase = new EglBase(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
116 eglBase.createDummyPbufferSurface(); 358 eglBase.createDummyPbufferSurface();
117 eglBase.makeCurrent(); 359 eglBase.makeCurrent();
118 360
119 oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); 361 oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
120 surfaceTexture = new SurfaceTexture(oesTextureId); 362 surfaceTexture = new SurfaceTexture(oesTextureId);
121 } 363 }
122 364
365 private ConvertYUV getConvertYUV() {
366 // convertYUV is assign once
367 if (convertYUV != null)
368 return convertYUV;
369
370 synchronized(this) {
371 if (convertYUV == null)
372 convertYUV = new ConvertYUV(eglBase.getContext());
373 return convertYUV;
374 }
375 }
376
123 /** 377 /**
124 * Start to stream textures to the given |listener|. 378 * Start to stream textures to the given |listener|.
125 * A Listener can only be set once. 379 * A Listener can only be set once.
126 */ 380 */
127 public void setListener(OnTextureFrameAvailableListener listener) { 381 public void setListener(OnTextureFrameAvailableListener listener) {
128 if (this.listener != null) { 382 if (this.listener != null) {
129 throw new IllegalStateException("SurfaceTextureHelper listener has already been set."); 383 throw new IllegalStateException("SurfaceTextureHelper listener has already been set.");
130 } 384 }
131 this.listener = listener; 385 this.listener = listener;
132 surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailab leListener() { 386 surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailab leListener() {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 * onTextureFrameAvailable() after this function returns. 454 * onTextureFrameAvailable() after this function returns.
201 */ 455 */
202 public void disconnect(Handler handler) { 456 public void disconnect(Handler handler) {
203 if (this.handler != handler) { 457 if (this.handler != handler) {
204 throw new IllegalStateException("Wrong handler."); 458 throw new IllegalStateException("Wrong handler.");
205 } 459 }
206 isOwningThread = true; 460 isOwningThread = true;
207 disconnect(); 461 disconnect();
208 } 462 }
209 463
464 public void textureToYUV(ByteBuffer buf,
465 int width, int height, int stride, int textureId, float [] transformMatrix ) {
466 Logging.e(TAG, "textureToYUV called");
perkj_webrtc 2015/12/07 12:53:50 remove logging.
467
468 if (textureId != oesTextureId)
469 throw new IllegalStateException("textureToByteBuffer called with unexpecte d textureId");
470
471 getConvertYUV().convert(buf, width, height, stride, textureId, transformMatr ix);
472 }
473
210 private void tryDeliverTextureFrame() { 474 private void tryDeliverTextureFrame() {
211 if (handler.getLooper().getThread() != Thread.currentThread()) { 475 if (handler.getLooper().getThread() != Thread.currentThread()) {
212 throw new IllegalStateException("Wrong thread."); 476 throw new IllegalStateException("Wrong thread.");
213 } 477 }
214 if (isQuitting || !hasPendingTexture || isTextureInUse) { 478 if (isQuitting || !hasPendingTexture || isTextureInUse) {
215 return; 479 return;
216 } 480 }
217 isTextureInUse = true; 481 isTextureInUse = true;
218 hasPendingTexture = false; 482 hasPendingTexture = false;
219 483
220 eglBase.makeCurrent(); 484 eglBase.makeCurrent();
221 surfaceTexture.updateTexImage(); 485 surfaceTexture.updateTexImage();
222 486
223 final float[] transformMatrix = new float[16]; 487 final float[] transformMatrix = new float[16];
224 surfaceTexture.getTransformMatrix(transformMatrix); 488 surfaceTexture.getTransformMatrix(transformMatrix);
225 final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_C REAM_SANDWICH) 489 final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_C REAM_SANDWICH)
226 ? surfaceTexture.getTimestamp() 490 ? surfaceTexture.getTimestamp()
227 : TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); 491 : TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
228 listener.onTextureFrameAvailable(oesTextureId, transformMatrix, timestampNs) ; 492 listener.onTextureFrameAvailable(oesTextureId, transformMatrix, timestampNs) ;
229 } 493 }
230 494
231 private void release() { 495 private void release() {
232 if (handler.getLooper().getThread() != Thread.currentThread()) { 496 if (handler.getLooper().getThread() != Thread.currentThread()) {
233 throw new IllegalStateException("Wrong thread."); 497 throw new IllegalStateException("Wrong thread.");
234 } 498 }
235 if (isTextureInUse || !isQuitting) { 499 if (isTextureInUse || !isQuitting) {
236 throw new IllegalStateException("Unexpected release."); 500 throw new IllegalStateException("Unexpected release.");
237 } 501 }
502 synchronized (this) {
503 if (convertYUV != null)
504 convertYUV.release();
505 }
238 eglBase.makeCurrent(); 506 eglBase.makeCurrent();
239 GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0); 507 GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0);
240 surfaceTexture.release(); 508 surfaceTexture.release();
241 eglBase.release(); 509 eglBase.release();
242 handler.getLooper().quit(); 510 handler.getLooper().quit();
243 } 511 }
244 } 512 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698