OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 RGBA conversion, instantiated on demand. | |
102 static private class ConvertRGBA { | |
magjed_webrtc
2015/12/03 12:33:58
I don't want to land a ConvertToRGBA until we need
nisse-webrtc
2015/12/03 13:35:20
I'll do. I wonder if it's worth the effort to keep
magjed_webrtc
2015/12/03 14:30:46
Yes, do that :) It is also saved in the patch hist
| |
103 private final EglBase eglBase; | |
104 private GlRectDrawer drawer; | |
105 ConvertRGBA (EGLContext sharedContext) { | |
106 eglBase = new EglBase(sharedContext, EglBase.CONFIG_PIXEL_BUFFER); | |
107 drawer = null; | |
108 } | |
109 | |
110 public void convert(ByteBuffer buf, | |
111 int width, int height, int textureId, float [] transformMatrix) { | |
112 synchronized(this) { | |
113 int size = 4 * width * height; | |
114 if (buf.capacity() < size) { | |
115 Logging.e(TAG, "needed size: " + size + ", available size: " + buf.cap acity()); | |
116 throw new IllegalStateException("ConvertRGBA.convert called with too s mall buffer"); | |
117 } | |
118 // Produce a frame buffer starting at top-left corner, not | |
119 // bottom-left. | |
120 transformMatrix = | |
121 RendererCommon.multiplyMatrices(transformMatrix, | |
122 RendererCommon.verticalFlipMatrix()); | |
123 | |
124 try { | |
125 // Reuse surface, if possible. | |
126 if (eglBase.hasSurface()) { | |
127 if (eglBase.surfaceWidth() != width || | |
128 eglBase.surfaceHeight() != height){ | |
129 eglBase.releaseSurface(); | |
130 eglBase.createPbufferSurface(width, height); | |
131 } | |
132 } else { | |
133 eglBase.createPbufferSurface(width, height); | |
134 } | |
135 | |
136 eglBase.makeCurrent(); | |
137 GLES20.glViewport(0, 0, width, height); | |
138 GlUtil.checkNoGLES2Error("Viewport"); | |
139 | |
140 if (drawer == null) | |
141 drawer = new GlRectDrawer(); | |
142 | |
143 drawer.drawOes(textureId, transformMatrix); | |
144 GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNS IGNED_BYTE, buf); | |
145 GlUtil.checkNoGLES2Error("glReadPixels"); | |
146 } | |
147 finally { | |
148 eglBase.detachCurrent(); | |
149 } | |
150 } | |
151 } | |
152 public void release() { | |
153 synchronized(this) { | |
154 eglBase.makeCurrent(); | |
155 if (drawer != null) | |
156 drawer.release(); | |
157 eglBase.release(); | |
158 } | |
159 } | |
160 } | |
161 | |
162 // State for YUV conversion, instantiated on demand. | |
163 static private class ConvertYUV { | |
164 private final EglBase eglBase; | |
165 private final GlShader shader; | |
166 private boolean released = false; | |
167 | |
168 // Vertex coordinates in Normalized Device Coordinates, i.e. | |
169 // (-1, -1) is bottom-left and (1, 1) is top-right. | |
170 private static final FloatBuffer DEVICE_RECTANGLE = | |
171 GlUtil.createFloatBuffer(new float[] { | |
172 -1.0f, -1.0f, // Bottom left. | |
173 1.0f, -1.0f, // Bottom right. | |
174 -1.0f, 1.0f, // Top left. | |
175 1.0f, 1.0f, // Top right. | |
176 }); | |
177 | |
178 // Texture coordinates - (0, 0) is bottom-left and (1, 1) is top-right. | |
179 private static final FloatBuffer TEXTURE_RECTANGLE = | |
180 GlUtil.createFloatBuffer(new float[] { | |
181 0.0f, 0.0f, // Bottom left. | |
182 1.0f, 0.0f, // Bottom right. | |
183 0.0f, 1.0f, // Top left. | |
184 1.0f, 1.0f // Top right. | |
185 }); | |
186 | |
187 private static final String VERTEX_SHADER = | |
188 "varying vec2 interp_tc;\n" | |
189 + "attribute vec4 in_pos;\n" | |
190 + "attribute vec4 in_tc;\n" | |
191 + "\n" | |
192 + "uniform mat4 texMatrix;\n" | |
193 + "\n" | |
194 + "void main() {\n" | |
195 + " gl_Position = in_pos;\n" | |
196 + " interp_tc = (texMatrix * in_tc).xy;\n" | |
197 + "}\n"; | |
198 | |
199 private static final String FRAGMENT_SHADER = | |
200 "#extension GL_OES_EGL_image_external : require\n" | |
201 + "precision mediump float;\n" | |
202 + "varying vec2 interp_tc;\n" | |
203 + "\n" | |
204 + "uniform samplerExternalOES oesTex;\n" | |
205 // Difference in texture coordinate corresponding to one | |
206 // sub-pixel in the x direction. | |
207 + "uniform vec2 xUnit;\n" | |
208 // Color conversion coefficients, including constant term | |
209 + "uniform vec4 coeffs;\n" | |
210 + "\n" | |
211 + "void main() {\n" | |
212 // TODO(nisse): Arrange color values into a matrix? | |
213 + " gl_FragColor.r = coeffs.a + dot(coeffs.rgb,\n" | |
214 + " texture2D(oesTex, interp_tc - 1.5 * xUnit).rgb);\n" | |
magjed_webrtc
2015/12/03 12:33:58
The alpha value from the texture2D should always b
| |
215 + " gl_FragColor.g = coeffs.a + dot(coeffs.rgb,\n" | |
216 + " texture2D(oesTex, interp_tc - 0.5 * xUnit).rgb);\n" | |
217 + " gl_FragColor.b = coeffs.a + dot(coeffs.rgb,\n" | |
218 + " texture2D(oesTex, interp_tc + 0.5 * xUnit).rgb);\n" | |
219 + " gl_FragColor.a = coeffs.a + dot(coeffs.rgb,\n" | |
220 + " texture2D(oesTex, interp_tc + 1.5 * xUnit).rgb);\n" | |
221 + "}\n"; | |
222 | |
223 private int texMatrixLoc; | |
224 private int xUnitLoc; | |
225 private int coeffsLoc;; | |
226 | |
227 ConvertYUV (EGLContext sharedContext) { | |
228 eglBase = new EglBase(sharedContext, EglBase.CONFIG_PIXEL_RGBA_BUFFER); | |
229 eglBase.createDummyPbufferSurface(); | |
230 eglBase.makeCurrent(); | |
231 try { | |
magjed_webrtc
2015/12/03 12:33:58
You don't need this try-finally. Detaching the EGL
nisse-webrtc
2015/12/04 09:40:34
Done.
| |
232 shader = new GlShader(VERTEX_SHADER, FRAGMENT_SHADER); | |
233 shader.useProgram(); | |
234 texMatrixLoc = shader.getUniformLocation("texMatrix"); | |
235 xUnitLoc = shader.getUniformLocation("xUnit"); | |
236 coeffsLoc = shader.getUniformLocation("coeffs"); | |
237 GLES20.glUniform1i(shader.getUniformLocation("oesTex"), 0); | |
238 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values."); | |
239 // Initialize vertex shader attributes. | |
240 shader.setVertexAttribArray("in_pos", 2, DEVICE_RECTANGLE); | |
241 // If the width is not a multiple of 4 pixels, the texture | |
242 // will be scaled up slightly and clipped at the right border. | |
243 shader.setVertexAttribArray("in_tc", 2, TEXTURE_RECTANGLE); | |
244 } | |
245 finally { | |
246 eglBase.detachCurrent(); | |
247 } | |
248 } | |
249 | |
250 public void convert(ByteBuffer buf, | |
251 int width, int height, int stride, int textureId, float [] transformMatr ix) { | |
magjed_webrtc
2015/12/03 12:33:58
why do you need a stride value? width/height shoul
nisse-webrtc
2015/12/03 13:35:20
To allow for some additional alignment, and at the
| |
252 synchronized(this) { | |
magjed_webrtc
2015/12/03 12:33:58
If the whole function is synchronized on |this|, y
nisse-webrtc
2015/12/03 13:35:20
Acknowledged.
nisse-webrtc
2015/12/04 09:40:34
Done.
| |
253 // TODO(nisse): For now only produces the Y plane. | |
254 if (released) { | |
255 throw new IllegalStateException( | |
256 "ConvertYUV.convert called on released object"); | |
257 } | |
258 if (stride % 8 != 0) { | |
259 throw new IllegalArgumentException( | |
260 "Invalid stride, must be a multiple of 8"); | |
magjed_webrtc
2015/12/03 12:33:58
why?
nisse-webrtc
2015/12/03 13:35:20
Because we do a split in half for drawing u and v,
| |
261 } | |
262 if (stride < width){ | |
263 throw new IllegalArgumentException( | |
264 "Invalid stride, must >= width"); | |
265 } | |
266 | |
267 int y_width = (width+3) / 4; | |
268 int uv_width = (width+7) / 8; | |
269 int uv_height = (height+1)/2; | |
270 int total_height = height + uv_height; | |
271 int size = stride * total_height; | |
272 | |
273 Logging.e(TAG, "width: " + width + ", height: " + height + | |
274 ", y-width: " + y_width + ", stride: " + stride); | |
275 | |
276 Logging.e(TAG, "matrix: \n " + transformMatrix[0] | |
277 + " " + transformMatrix[1] | |
278 + " " + transformMatrix[2] | |
279 + " " + transformMatrix[3] | |
280 + "\n " + transformMatrix[4] | |
281 + " " + transformMatrix[5] | |
282 + " " + transformMatrix[6] | |
283 + " " + transformMatrix[7] | |
284 + "\n " + transformMatrix[8] | |
285 + " " + transformMatrix[9] | |
286 + " " + transformMatrix[10] | |
287 + " " + transformMatrix[11] | |
288 + "\n " + transformMatrix[12] | |
289 + " " + transformMatrix[13] | |
290 + " " + transformMatrix[14] | |
291 + " " + transformMatrix[15]); | |
292 | |
293 if (buf.capacity() < size) { | |
294 Logging.e(TAG, "needed size: " + size + ", available size: " + buf.cap acity()); | |
295 throw new IllegalStateException("ConvertYUV.convert called with too sm all buffer"); | |
magjed_webrtc
2015/12/03 12:33:58
IllegalArgument
nisse-webrtc
2015/12/04 09:40:34
Done.
| |
296 } | |
297 // Produce a frame buffer starting at top-left corner, not | |
298 // bottom-left. | |
299 transformMatrix = | |
300 RendererCommon.multiplyMatrices(transformMatrix, | |
301 RendererCommon.verticalFlipMatrix()); | |
302 | |
303 try { | |
304 // Reuse surface, if possible. TODO(nisse): Add an eglBase | |
305 // helper function, say, makeCurrentWithSize. | |
306 if (eglBase.hasSurface()) { | |
307 if (eglBase.surfaceWidth() != stride/4 || | |
308 eglBase.surfaceHeight() != total_height){ | |
309 eglBase.releaseSurface(); | |
310 eglBase.createPbufferSurface(stride/4, total_height); | |
311 } | |
312 } else { | |
313 eglBase.createPbufferSurface(stride/4, total_height); | |
314 } | |
315 | |
316 eglBase.makeCurrent(); | |
317 | |
318 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values.") ; | |
319 | |
320 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); | |
321 GlUtil.checkNoGLES2Error("Active texture"); | |
322 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); | |
323 GLES20.glUniformMatrix4fv(texMatrixLoc, 1, false, transformMatrix, 0); | |
324 | |
325 // Draw Y */ | |
326 GLES20.glViewport(0, 0, y_width, height); | |
327 GlUtil.checkNoGLES2Error("Viewport"); | |
328 // Matrix * (1;0;0;0) / width. Note that opengl uses column major orde r. | |
329 GLES20.glUniform2f(xUnitLoc, | |
330 transformMatrix[0] / width, | |
331 transformMatrix[1] / width); | |
332 // Y'UV444 to RGB888, see | |
333 // https://en.wikipedia.org/wiki/YUV#Y.27UV444_to_RGB888_conversion | |
334 GLES20.glUniform4f(coeffsLoc, 0.299f, 0.587f, 0.114f, 0.0f); | |
335 GlUtil.checkNoGLES2Error("Binding texture and matrix"); | |
336 | |
337 Logging.e(TAG, "ConvertYUV: glDrawArrays"); | |
338 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); | |
339 GlUtil.checkNoGLES2Error("glDrawArrays, Y"); | |
340 | |
341 // Draw U */ | |
342 GLES20.glViewport(0, height, uv_width, uv_height); | |
343 GlUtil.checkNoGLES2Error("Viewport"); | |
344 // Matrix * (1;0;0;0) / (2*width). Note that opengl uses column major order. | |
345 GLES20.glUniform2f(xUnitLoc, | |
346 transformMatrix[0] / (2.0f*width), | |
347 transformMatrix[1] / (2.0f*width)); | |
348 /* Use ITU-R coefficients for U and V */ | |
349 GLES20.glUniform4f(coeffsLoc, -0.169f, -0.331f, 0.499f, 0.5f); | |
350 GlUtil.checkNoGLES2Error("Binding texture and matrix"); | |
351 | |
352 Logging.e(TAG, "ConvertYUV: glDrawArrays"); | |
353 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); | |
354 GlUtil.checkNoGLES2Error("glDrawArrays, U"); | |
355 | |
356 // Draw V */ | |
357 GLES20.glViewport(stride/8, height, uv_width, uv_height); | |
358 GlUtil.checkNoGLES2Error("Viewport"); | |
359 /* Use ITU-R coefficients for U and V */ | |
360 GLES20.glUniform4f(coeffsLoc, 0.499f, -0.418f, -0.0813f, 0.5f); | |
361 GlUtil.checkNoGLES2Error("Binding texture and matrix"); | |
362 | |
363 Logging.e(TAG, "ConvertYUV: glDrawArrays"); | |
364 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); | |
365 GlUtil.checkNoGLES2Error("glDrawArrays, V"); | |
366 | |
367 Logging.e(TAG, "ConvertYUV: glReadPixels"); | |
368 GLES20.glReadPixels(0, 0, stride/4, total_height, GLES20.GL_RGBA, | |
369 GLES20.GL_UNSIGNED_BYTE, buf); | |
370 GlUtil.checkNoGLES2Error("glReadPixels"); | |
371 | |
372 // Unbind texture. Reportedly needed on some devices to get | |
373 // the texture updated from the camera. | |
374 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); | |
375 } | |
376 finally { | |
377 eglBase.detachCurrent(); | |
378 } | |
379 } | |
380 } | |
381 | |
382 public void release() { | |
383 synchronized(this) { | |
384 released = true; | |
385 shader.release(); | |
386 eglBase.makeCurrent(); | |
magjed_webrtc
2015/12/03 12:33:58
shader.release() need an EGLContext current, so yo
nisse-webrtc
2015/12/04 09:40:34
Done.
| |
387 eglBase.release(); | |
388 } | |
389 } | |
390 } | |
391 | |
97 private final Handler handler; | 392 private final Handler handler; |
98 private boolean isOwningThread; | 393 private boolean isOwningThread; |
99 private final EglBase eglBase; | 394 private final EglBase eglBase; |
100 private final SurfaceTexture surfaceTexture; | 395 private final SurfaceTexture surfaceTexture; |
101 private final int oesTextureId; | 396 private final int oesTextureId; |
397 private ConvertRGBA convertRGBA; | |
398 private ConvertYUV convertYUV; | |
399 | |
102 private OnTextureFrameAvailableListener listener; | 400 private OnTextureFrameAvailableListener listener; |
103 // The possible states of this class. | 401 // The possible states of this class. |
104 private boolean hasPendingTexture = false; | 402 private boolean hasPendingTexture = false; |
105 private boolean isTextureInUse = false; | 403 private boolean isTextureInUse = false; |
106 private boolean isQuitting = false; | 404 private boolean isQuitting = false; |
107 | 405 |
108 private SurfaceTextureHelper(EGLContext sharedContext, Handler handler, boolea n isOwningThread) { | 406 private SurfaceTextureHelper(EGLContext sharedContext, Handler handler, boolea n isOwningThread) { |
109 if (handler.getLooper().getThread() != Thread.currentThread()) { | 407 if (handler.getLooper().getThread() != Thread.currentThread()) { |
110 throw new IllegalStateException("SurfaceTextureHelper must be created on t he handler thread"); | 408 throw new IllegalStateException("SurfaceTextureHelper must be created on t he handler thread"); |
111 } | 409 } |
112 this.handler = handler; | 410 this.handler = handler; |
113 this.isOwningThread = isOwningThread; | 411 this.isOwningThread = isOwningThread; |
114 | 412 |
115 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); | 413 eglBase = new EglBase(sharedContext, EglBase.CONFIG_PIXEL_BUFFER); |
116 eglBase.createDummyPbufferSurface(); | 414 eglBase.createDummyPbufferSurface(); |
117 eglBase.makeCurrent(); | 415 eglBase.makeCurrent(); |
118 | 416 |
119 oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); | 417 oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); |
120 surfaceTexture = new SurfaceTexture(oesTextureId); | 418 surfaceTexture = new SurfaceTexture(oesTextureId); |
121 } | 419 } |
122 | 420 |
421 private ConvertRGBA getConvertRGBA() { | |
422 // convertRGBA is assign once | |
423 if (convertRGBA != null) | |
424 return convertRGBA; | |
425 | |
426 synchronized(this) { | |
427 if (convertRGBA == null) | |
428 convertRGBA = new ConvertRGBA(eglBase.getContext()); | |
429 return convertRGBA; | |
430 } | |
431 } | |
432 | |
433 private ConvertYUV getConvertYUV() { | |
434 // convertYUV is assign once | |
435 if (convertYUV != null) | |
436 return convertYUV; | |
437 | |
438 synchronized(this) { | |
439 if (convertYUV == null) | |
440 convertYUV = new ConvertYUV(eglBase.getContext()); | |
441 return convertYUV; | |
442 } | |
443 } | |
444 | |
123 /** | 445 /** |
124 * Start to stream textures to the given |listener|. | 446 * Start to stream textures to the given |listener|. |
125 * A Listener can only be set once. | 447 * A Listener can only be set once. |
126 */ | 448 */ |
127 public void setListener(OnTextureFrameAvailableListener listener) { | 449 public void setListener(OnTextureFrameAvailableListener listener) { |
128 if (this.listener != null) { | 450 if (this.listener != null) { |
129 throw new IllegalStateException("SurfaceTextureHelper listener has already been set."); | 451 throw new IllegalStateException("SurfaceTextureHelper listener has already been set."); |
130 } | 452 } |
131 this.listener = listener; | 453 this.listener = listener; |
132 surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailab leListener() { | 454 surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailab leListener() { |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
200 * onTextureFrameAvailable() after this function returns. | 522 * onTextureFrameAvailable() after this function returns. |
201 */ | 523 */ |
202 public void disconnect(Handler handler) { | 524 public void disconnect(Handler handler) { |
203 if (this.handler != handler) { | 525 if (this.handler != handler) { |
204 throw new IllegalStateException("Wrong handler."); | 526 throw new IllegalStateException("Wrong handler."); |
205 } | 527 } |
206 isOwningThread = true; | 528 isOwningThread = true; |
207 disconnect(); | 529 disconnect(); |
208 } | 530 } |
209 | 531 |
532 public void textureToRGBA(ByteBuffer buf, | |
533 int width, int height, int textureId, float [] transformMatrix) { | |
534 | |
535 Logging.e(TAG, "textureToRGBA called"); | |
536 | |
537 if (textureId != oesTextureId) | |
538 throw new IllegalStateException("textureToByteBuffer called with unexpecte d textureId"); | |
539 | |
540 getConvertRGBA().convert(buf, width, height, textureId, transformMatrix); | |
541 } | |
542 | |
543 public void textureToYUV(ByteBuffer buf, | |
544 int width, int height, int stride, int textureId, float [] transformMatrix ) { | |
545 Logging.e(TAG, "textureToYUV called"); | |
546 | |
547 if (textureId != oesTextureId) | |
548 throw new IllegalStateException("textureToByteBuffer called with unexpecte d textureId"); | |
549 | |
550 getConvertYUV().convert(buf, width, height, stride, textureId, transformMatr ix); | |
551 } | |
552 | |
210 private void tryDeliverTextureFrame() { | 553 private void tryDeliverTextureFrame() { |
211 if (handler.getLooper().getThread() != Thread.currentThread()) { | 554 if (handler.getLooper().getThread() != Thread.currentThread()) { |
212 throw new IllegalStateException("Wrong thread."); | 555 throw new IllegalStateException("Wrong thread."); |
213 } | 556 } |
214 if (isQuitting || !hasPendingTexture || isTextureInUse) { | 557 if (isQuitting || !hasPendingTexture || isTextureInUse) { |
215 return; | 558 return; |
216 } | 559 } |
217 isTextureInUse = true; | 560 isTextureInUse = true; |
218 hasPendingTexture = false; | 561 hasPendingTexture = false; |
219 | 562 |
220 eglBase.makeCurrent(); | 563 eglBase.makeCurrent(); |
221 surfaceTexture.updateTexImage(); | 564 surfaceTexture.updateTexImage(); |
222 | 565 |
223 final float[] transformMatrix = new float[16]; | 566 final float[] transformMatrix = new float[16]; |
224 surfaceTexture.getTransformMatrix(transformMatrix); | 567 surfaceTexture.getTransformMatrix(transformMatrix); |
225 final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_C REAM_SANDWICH) | 568 final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_C REAM_SANDWICH) |
226 ? surfaceTexture.getTimestamp() | 569 ? surfaceTexture.getTimestamp() |
227 : TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); | 570 : TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime()); |
228 listener.onTextureFrameAvailable(oesTextureId, transformMatrix, timestampNs) ; | 571 listener.onTextureFrameAvailable(oesTextureId, transformMatrix, timestampNs) ; |
229 } | 572 } |
230 | 573 |
231 private void release() { | 574 private void release() { |
232 if (handler.getLooper().getThread() != Thread.currentThread()) { | 575 if (handler.getLooper().getThread() != Thread.currentThread()) { |
233 throw new IllegalStateException("Wrong thread."); | 576 throw new IllegalStateException("Wrong thread."); |
234 } | 577 } |
235 if (isTextureInUse || !isQuitting) { | 578 if (isTextureInUse || !isQuitting) { |
236 throw new IllegalStateException("Unexpected release."); | 579 throw new IllegalStateException("Unexpected release."); |
237 } | 580 } |
581 synchronized (this) { | |
582 if (convertRGBA != null) | |
583 convertRGBA.release(); | |
584 if (convertYUV != null) | |
585 convertYUV.release(); | |
586 } | |
238 eglBase.makeCurrent(); | 587 eglBase.makeCurrent(); |
239 GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0); | 588 GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0); |
240 surfaceTexture.release(); | 589 surfaceTexture.release(); |
241 eglBase.release(); | 590 eglBase.release(); |
242 handler.getLooper().quit(); | 591 handler.getLooper().quit(); |
243 } | 592 } |
244 } | 593 } |
OLD | NEW |