| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2015 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 package org.webrtc; | |
| 11 | |
| 12 import android.graphics.SurfaceTexture; | |
| 13 import android.opengl.GLES20; | |
| 14 import android.os.SystemClock; | |
| 15 import android.test.ActivityTestCase; | |
| 16 import android.test.suitebuilder.annotation.MediumTest; | |
| 17 import android.test.suitebuilder.annotation.SmallTest; | |
| 18 | |
| 19 import java.nio.ByteBuffer; | |
| 20 import java.util.concurrent.CountDownLatch; | |
| 21 | |
| 22 public final class SurfaceTextureHelperTest extends ActivityTestCase { | |
| 23 /** | |
| 24 * Mock texture listener with blocking wait functionality. | |
| 25 */ | |
| 26 public static final class MockTextureListener | |
| 27 implements SurfaceTextureHelper.OnTextureFrameAvailableListener { | |
| 28 public int oesTextureId; | |
| 29 public float[] transformMatrix; | |
| 30 private boolean hasNewFrame = false; | |
| 31 // Thread where frames are expected to be received on. | |
| 32 private final Thread expectedThread; | |
| 33 | |
| 34 MockTextureListener() { | |
| 35 this.expectedThread = null; | |
| 36 } | |
| 37 | |
| 38 MockTextureListener(Thread expectedThread) { | |
| 39 this.expectedThread = expectedThread; | |
| 40 } | |
| 41 | |
| 42 @Override | |
| 43 public synchronized void onTextureFrameAvailable( | |
| 44 int oesTextureId, float[] transformMatrix, long timestampNs) { | |
| 45 if (expectedThread != null && Thread.currentThread() != expectedThread) { | |
| 46 throw new IllegalStateException("onTextureFrameAvailable called on wrong
thread."); | |
| 47 } | |
| 48 this.oesTextureId = oesTextureId; | |
| 49 this.transformMatrix = transformMatrix; | |
| 50 hasNewFrame = true; | |
| 51 notifyAll(); | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * Wait indefinitely for a new frame. | |
| 56 */ | |
| 57 public synchronized void waitForNewFrame() throws InterruptedException { | |
| 58 while (!hasNewFrame) { | |
| 59 wait(); | |
| 60 } | |
| 61 hasNewFrame = false; | |
| 62 } | |
| 63 | |
| 64 /** | |
| 65 * Wait for a new frame, or until the specified timeout elapses. Returns tru
e if a new frame was | |
| 66 * received before the timeout. | |
| 67 */ | |
| 68 public synchronized boolean waitForNewFrame(final long timeoutMs) throws Int
erruptedException { | |
| 69 final long startTimeMs = SystemClock.elapsedRealtime(); | |
| 70 long timeRemainingMs = timeoutMs; | |
| 71 while (!hasNewFrame && timeRemainingMs > 0) { | |
| 72 wait(timeRemainingMs); | |
| 73 final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs; | |
| 74 timeRemainingMs = timeoutMs - elapsedTimeMs; | |
| 75 } | |
| 76 final boolean didReceiveFrame = hasNewFrame; | |
| 77 hasNewFrame = false; | |
| 78 return didReceiveFrame; | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 /** Assert that two integers are close, with difference at most | |
| 83 * {@code threshold}. */ | |
| 84 public static void assertClose(int threshold, int expected, int actual) { | |
| 85 if (Math.abs(expected - actual) <= threshold) | |
| 86 return; | |
| 87 failNotEquals("Not close enough, threshold " + threshold, expected, actual); | |
| 88 } | |
| 89 | |
| 90 /** | |
| 91 * Test normal use by receiving three uniform texture frames. Texture frames a
re returned as early | |
| 92 * as possible. The texture pixel values are inspected by drawing the texture
frame to a pixel | |
| 93 * buffer and reading it back with glReadPixels(). | |
| 94 */ | |
| 95 @MediumTest | |
| 96 public static void testThreeConstantColorFrames() throws InterruptedException
{ | |
| 97 final int width = 16; | |
| 98 final int height = 16; | |
| 99 // Create EGL base with a pixel buffer as display output. | |
| 100 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER); | |
| 101 eglBase.createPbufferSurface(width, height); | |
| 102 final GlRectDrawer drawer = new GlRectDrawer(); | |
| 103 | |
| 104 // Create SurfaceTextureHelper and listener. | |
| 105 final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.creat
e( | |
| 106 "SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext(
)); | |
| 107 final MockTextureListener listener = new MockTextureListener(); | |
| 108 surfaceTextureHelper.startListening(listener); | |
| 109 surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height)
; | |
| 110 | |
| 111 // Create resources for stubbing an OES texture producer. |eglOesBase| has t
he SurfaceTexture in | |
| 112 // |surfaceTextureHelper| as the target EGLSurface. | |
| 113 final EglBase eglOesBase = EglBase.create(eglBase.getEglBaseContext(), EglBa
se.CONFIG_PLAIN); | |
| 114 eglOesBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 115 assertEquals(eglOesBase.surfaceWidth(), width); | |
| 116 assertEquals(eglOesBase.surfaceHeight(), height); | |
| 117 | |
| 118 final int red[] = new int[] {79, 144, 185}; | |
| 119 final int green[] = new int[] {66, 210, 162}; | |
| 120 final int blue[] = new int[] {161, 117, 158}; | |
| 121 // Draw three frames. | |
| 122 for (int i = 0; i < 3; ++i) { | |
| 123 // Draw a constant color frame onto the SurfaceTexture. | |
| 124 eglOesBase.makeCurrent(); | |
| 125 GLES20.glClearColor(red[i] / 255.0f, green[i] / 255.0f, blue[i] / 255.0f,
1.0f); | |
| 126 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 127 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 128 eglOesBase.swapBuffers(); | |
| 129 | |
| 130 // Wait for an OES texture to arrive and draw it onto the pixel buffer. | |
| 131 listener.waitForNewFrame(); | |
| 132 eglBase.makeCurrent(); | |
| 133 drawer.drawOes( | |
| 134 listener.oesTextureId, listener.transformMatrix, width, height, 0, 0,
width, height); | |
| 135 | |
| 136 surfaceTextureHelper.returnTextureFrame(); | |
| 137 | |
| 138 // Download the pixels in the pixel buffer as RGBA. Not all platforms supp
ort RGB, e.g. | |
| 139 // Nexus 9. | |
| 140 final ByteBuffer rgbaData = ByteBuffer.allocateDirect(width * height * 4); | |
| 141 GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNE
D_BYTE, rgbaData); | |
| 142 GlUtil.checkNoGLES2Error("glReadPixels"); | |
| 143 | |
| 144 // Assert rendered image is expected constant color. | |
| 145 while (rgbaData.hasRemaining()) { | |
| 146 assertEquals(rgbaData.get() & 0xFF, red[i]); | |
| 147 assertEquals(rgbaData.get() & 0xFF, green[i]); | |
| 148 assertEquals(rgbaData.get() & 0xFF, blue[i]); | |
| 149 assertEquals(rgbaData.get() & 0xFF, 255); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 drawer.release(); | |
| 154 surfaceTextureHelper.dispose(); | |
| 155 eglBase.release(); | |
| 156 } | |
| 157 | |
| 158 /** | |
| 159 * Test disposing the SurfaceTextureHelper while holding a pending texture fra
me. The pending | |
| 160 * texture frame should still be valid, and this is tested by drawing the text
ure frame to a pixel | |
| 161 * buffer and reading it back with glReadPixels(). | |
| 162 */ | |
| 163 @MediumTest | |
| 164 public static void testLateReturnFrame() throws InterruptedException { | |
| 165 final int width = 16; | |
| 166 final int height = 16; | |
| 167 // Create EGL base with a pixel buffer as display output. | |
| 168 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER); | |
| 169 eglBase.createPbufferSurface(width, height); | |
| 170 | |
| 171 // Create SurfaceTextureHelper and listener. | |
| 172 final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.creat
e( | |
| 173 "SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext(
)); | |
| 174 final MockTextureListener listener = new MockTextureListener(); | |
| 175 surfaceTextureHelper.startListening(listener); | |
| 176 surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height)
; | |
| 177 | |
| 178 // Create resources for stubbing an OES texture producer. |eglOesBase| has t
he SurfaceTexture in | |
| 179 // |surfaceTextureHelper| as the target EGLSurface. | |
| 180 final EglBase eglOesBase = EglBase.create(eglBase.getEglBaseContext(), EglBa
se.CONFIG_PLAIN); | |
| 181 eglOesBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 182 assertEquals(eglOesBase.surfaceWidth(), width); | |
| 183 assertEquals(eglOesBase.surfaceHeight(), height); | |
| 184 | |
| 185 final int red = 79; | |
| 186 final int green = 66; | |
| 187 final int blue = 161; | |
| 188 // Draw a constant color frame onto the SurfaceTexture. | |
| 189 eglOesBase.makeCurrent(); | |
| 190 GLES20.glClearColor(red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f); | |
| 191 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 192 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 193 eglOesBase.swapBuffers(); | |
| 194 eglOesBase.release(); | |
| 195 | |
| 196 // Wait for OES texture frame. | |
| 197 listener.waitForNewFrame(); | |
| 198 // Diconnect while holding the frame. | |
| 199 surfaceTextureHelper.dispose(); | |
| 200 | |
| 201 // Draw the pending texture frame onto the pixel buffer. | |
| 202 eglBase.makeCurrent(); | |
| 203 final GlRectDrawer drawer = new GlRectDrawer(); | |
| 204 drawer.drawOes( | |
| 205 listener.oesTextureId, listener.transformMatrix, width, height, 0, 0, wi
dth, height); | |
| 206 drawer.release(); | |
| 207 | |
| 208 // Download the pixels in the pixel buffer as RGBA. Not all platforms suppor
t RGB, e.g. Nexus 9. | |
| 209 final ByteBuffer rgbaData = ByteBuffer.allocateDirect(width * height * 4); | |
| 210 GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_
BYTE, rgbaData); | |
| 211 GlUtil.checkNoGLES2Error("glReadPixels"); | |
| 212 eglBase.release(); | |
| 213 | |
| 214 // Assert rendered image is expected constant color. | |
| 215 while (rgbaData.hasRemaining()) { | |
| 216 assertEquals(rgbaData.get() & 0xFF, red); | |
| 217 assertEquals(rgbaData.get() & 0xFF, green); | |
| 218 assertEquals(rgbaData.get() & 0xFF, blue); | |
| 219 assertEquals(rgbaData.get() & 0xFF, 255); | |
| 220 } | |
| 221 // Late frame return after everything has been disposed and released. | |
| 222 surfaceTextureHelper.returnTextureFrame(); | |
| 223 } | |
| 224 | |
| 225 /** | |
| 226 * Test disposing the SurfaceTextureHelper, but keep trying to produce more te
xture frames. No | |
| 227 * frames should be delivered to the listener. | |
| 228 */ | |
| 229 @MediumTest | |
| 230 public static void testDispose() throws InterruptedException { | |
| 231 // Create SurfaceTextureHelper and listener. | |
| 232 final SurfaceTextureHelper surfaceTextureHelper = | |
| 233 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 234 final MockTextureListener listener = new MockTextureListener(); | |
| 235 surfaceTextureHelper.startListening(listener); | |
| 236 // Create EglBase with the SurfaceTexture as target EGLSurface. | |
| 237 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN); | |
| 238 eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 239 eglBase.makeCurrent(); | |
| 240 // Assert no frame has been received yet. | |
| 241 assertFalse(listener.waitForNewFrame(1)); | |
| 242 // Draw and wait for one frame. | |
| 243 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 244 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 245 eglBase.swapBuffers(); | |
| 246 listener.waitForNewFrame(); | |
| 247 surfaceTextureHelper.returnTextureFrame(); | |
| 248 | |
| 249 // Dispose - we should not receive any textures after this. | |
| 250 surfaceTextureHelper.dispose(); | |
| 251 | |
| 252 // Draw one frame. | |
| 253 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 254 eglBase.swapBuffers(); | |
| 255 // swapBuffers() should not trigger onTextureFrameAvailable() because dispos
ed has been called. | |
| 256 // Assert that no OES texture was delivered. | |
| 257 assertFalse(listener.waitForNewFrame(500)); | |
| 258 | |
| 259 eglBase.release(); | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * Test disposing the SurfaceTextureHelper immediately after is has been setup
to use a | |
| 264 * shared context. No frames should be delivered to the listener. | |
| 265 */ | |
| 266 @SmallTest | |
| 267 public static void testDisposeImmediately() { | |
| 268 final SurfaceTextureHelper surfaceTextureHelper = | |
| 269 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 270 surfaceTextureHelper.dispose(); | |
| 271 } | |
| 272 | |
| 273 /** | |
| 274 * Call stopListening(), but keep trying to produce more texture frames. No fr
ames should be | |
| 275 * delivered to the listener. | |
| 276 */ | |
| 277 @MediumTest | |
| 278 public static void testStopListening() throws InterruptedException { | |
| 279 // Create SurfaceTextureHelper and listener. | |
| 280 final SurfaceTextureHelper surfaceTextureHelper = | |
| 281 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 282 final MockTextureListener listener = new MockTextureListener(); | |
| 283 surfaceTextureHelper.startListening(listener); | |
| 284 // Create EglBase with the SurfaceTexture as target EGLSurface. | |
| 285 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN); | |
| 286 eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 287 eglBase.makeCurrent(); | |
| 288 // Assert no frame has been received yet. | |
| 289 assertFalse(listener.waitForNewFrame(1)); | |
| 290 // Draw and wait for one frame. | |
| 291 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 292 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 293 eglBase.swapBuffers(); | |
| 294 listener.waitForNewFrame(); | |
| 295 surfaceTextureHelper.returnTextureFrame(); | |
| 296 | |
| 297 // Stop listening - we should not receive any textures after this. | |
| 298 surfaceTextureHelper.stopListening(); | |
| 299 | |
| 300 // Draw one frame. | |
| 301 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 302 eglBase.swapBuffers(); | |
| 303 // swapBuffers() should not trigger onTextureFrameAvailable() because dispos
ed has been called. | |
| 304 // Assert that no OES texture was delivered. | |
| 305 assertFalse(listener.waitForNewFrame(500)); | |
| 306 | |
| 307 surfaceTextureHelper.dispose(); | |
| 308 eglBase.release(); | |
| 309 } | |
| 310 | |
| 311 /** | |
| 312 * Test stopListening() immediately after the SurfaceTextureHelper has been se
tup. | |
| 313 */ | |
| 314 @SmallTest | |
| 315 public static void testStopListeningImmediately() throws InterruptedException
{ | |
| 316 final SurfaceTextureHelper surfaceTextureHelper = | |
| 317 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 318 final MockTextureListener listener = new MockTextureListener(); | |
| 319 surfaceTextureHelper.startListening(listener); | |
| 320 surfaceTextureHelper.stopListening(); | |
| 321 surfaceTextureHelper.dispose(); | |
| 322 } | |
| 323 | |
| 324 /** | |
| 325 * Test stopListening() immediately after the SurfaceTextureHelper has been se
tup on the handler | |
| 326 * thread. | |
| 327 */ | |
| 328 @SmallTest | |
| 329 public static void testStopListeningImmediatelyOnHandlerThread() throws Interr
uptedException { | |
| 330 final SurfaceTextureHelper surfaceTextureHelper = | |
| 331 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 332 final MockTextureListener listener = new MockTextureListener(); | |
| 333 | |
| 334 final CountDownLatch stopListeningBarrier = new CountDownLatch(1); | |
| 335 final CountDownLatch stopListeningBarrierDone = new CountDownLatch(1); | |
| 336 // Start by posting to the handler thread to keep it occupied. | |
| 337 surfaceTextureHelper.getHandler().post(new Runnable() { | |
| 338 @Override | |
| 339 public void run() { | |
| 340 ThreadUtils.awaitUninterruptibly(stopListeningBarrier); | |
| 341 surfaceTextureHelper.stopListening(); | |
| 342 stopListeningBarrierDone.countDown(); | |
| 343 } | |
| 344 }); | |
| 345 | |
| 346 // startListening() is asynchronous and will post to the occupied handler th
read. | |
| 347 surfaceTextureHelper.startListening(listener); | |
| 348 // Wait for stopListening() to be called on the handler thread. | |
| 349 stopListeningBarrier.countDown(); | |
| 350 stopListeningBarrierDone.await(); | |
| 351 // Wait until handler thread is idle to try to catch late startListening() c
all. | |
| 352 final CountDownLatch barrier = new CountDownLatch(1); | |
| 353 surfaceTextureHelper.getHandler().post(new Runnable() { | |
| 354 @Override | |
| 355 public void run() { | |
| 356 barrier.countDown(); | |
| 357 } | |
| 358 }); | |
| 359 ThreadUtils.awaitUninterruptibly(barrier); | |
| 360 // Previous startListening() call should never have taken place and it shoul
d be ok to call it | |
| 361 // again. | |
| 362 surfaceTextureHelper.startListening(listener); | |
| 363 | |
| 364 surfaceTextureHelper.dispose(); | |
| 365 } | |
| 366 | |
| 367 /** | |
| 368 * Test calling startListening() with a new listener after stopListening() has
been called. | |
| 369 */ | |
| 370 @MediumTest | |
| 371 public static void testRestartListeningWithNewListener() throws InterruptedExc
eption { | |
| 372 // Create SurfaceTextureHelper and listener. | |
| 373 final SurfaceTextureHelper surfaceTextureHelper = | |
| 374 SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */
, null); | |
| 375 final MockTextureListener listener1 = new MockTextureListener(); | |
| 376 surfaceTextureHelper.startListening(listener1); | |
| 377 // Create EglBase with the SurfaceTexture as target EGLSurface. | |
| 378 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN); | |
| 379 eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 380 eglBase.makeCurrent(); | |
| 381 // Assert no frame has been received yet. | |
| 382 assertFalse(listener1.waitForNewFrame(1)); | |
| 383 // Draw and wait for one frame. | |
| 384 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 385 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 386 eglBase.swapBuffers(); | |
| 387 listener1.waitForNewFrame(); | |
| 388 surfaceTextureHelper.returnTextureFrame(); | |
| 389 | |
| 390 // Stop listening - |listener1| should not receive any textures after this. | |
| 391 surfaceTextureHelper.stopListening(); | |
| 392 | |
| 393 // Connect different listener. | |
| 394 final MockTextureListener listener2 = new MockTextureListener(); | |
| 395 surfaceTextureHelper.startListening(listener2); | |
| 396 // Assert no frame has been received yet. | |
| 397 assertFalse(listener2.waitForNewFrame(1)); | |
| 398 | |
| 399 // Draw one frame. | |
| 400 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 401 eglBase.swapBuffers(); | |
| 402 | |
| 403 // Check that |listener2| received the frame, and not |listener1|. | |
| 404 listener2.waitForNewFrame(); | |
| 405 assertFalse(listener1.waitForNewFrame(1)); | |
| 406 | |
| 407 surfaceTextureHelper.returnTextureFrame(); | |
| 408 | |
| 409 surfaceTextureHelper.dispose(); | |
| 410 eglBase.release(); | |
| 411 } | |
| 412 | |
| 413 @MediumTest | |
| 414 public static void testTexturetoYUV() throws InterruptedException { | |
| 415 final int width = 16; | |
| 416 final int height = 16; | |
| 417 | |
| 418 final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN); | |
| 419 | |
| 420 // Create SurfaceTextureHelper and listener. | |
| 421 final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.creat
e( | |
| 422 "SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext(
)); | |
| 423 final MockTextureListener listener = new MockTextureListener(); | |
| 424 surfaceTextureHelper.startListening(listener); | |
| 425 surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height)
; | |
| 426 | |
| 427 // Create resources for stubbing an OES texture producer. |eglBase| has the
SurfaceTexture in | |
| 428 // |surfaceTextureHelper| as the target EGLSurface. | |
| 429 | |
| 430 eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture()); | |
| 431 assertEquals(eglBase.surfaceWidth(), width); | |
| 432 assertEquals(eglBase.surfaceHeight(), height); | |
| 433 | |
| 434 final int red[] = new int[] {79, 144, 185}; | |
| 435 final int green[] = new int[] {66, 210, 162}; | |
| 436 final int blue[] = new int[] {161, 117, 158}; | |
| 437 | |
| 438 final int ref_y[] = new int[] {81, 180, 168}; | |
| 439 final int ref_u[] = new int[] {173, 93, 122}; | |
| 440 final int ref_v[] = new int[] {127, 103, 140}; | |
| 441 | |
| 442 // Draw three frames. | |
| 443 for (int i = 0; i < 3; ++i) { | |
| 444 // Draw a constant color frame onto the SurfaceTexture. | |
| 445 eglBase.makeCurrent(); | |
| 446 GLES20.glClearColor(red[i] / 255.0f, green[i] / 255.0f, blue[i] / 255.0f,
1.0f); | |
| 447 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
| 448 // swapBuffers() will ultimately trigger onTextureFrameAvailable(). | |
| 449 eglBase.swapBuffers(); | |
| 450 | |
| 451 // Wait for an OES texture to arrive. | |
| 452 listener.waitForNewFrame(); | |
| 453 | |
| 454 // Memory layout: Lines are 16 bytes. First 16 lines are | |
| 455 // the Y data. These are followed by 8 lines with 8 bytes of U | |
| 456 // data on the left and 8 bytes of V data on the right. | |
| 457 // | |
| 458 // Offset | |
| 459 // 0 YYYYYYYY YYYYYYYY | |
| 460 // 16 YYYYYYYY YYYYYYYY | |
| 461 // ... | |
| 462 // 240 YYYYYYYY YYYYYYYY | |
| 463 // 256 UUUUUUUU VVVVVVVV | |
| 464 // 272 UUUUUUUU VVVVVVVV | |
| 465 // ... | |
| 466 // 368 UUUUUUUU VVVVVVVV | |
| 467 // 384 buffer end | |
| 468 ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 3 / 2); | |
| 469 surfaceTextureHelper.textureToYUV( | |
| 470 buffer, width, height, width, listener.oesTextureId, listener.transfor
mMatrix); | |
| 471 | |
| 472 surfaceTextureHelper.returnTextureFrame(); | |
| 473 | |
| 474 // Allow off-by-one differences due to different rounding. | |
| 475 while (buffer.position() < width * height) { | |
| 476 assertClose(1, buffer.get() & 0xff, ref_y[i]); | |
| 477 } | |
| 478 while (buffer.hasRemaining()) { | |
| 479 if (buffer.position() % width < width / 2) | |
| 480 assertClose(1, buffer.get() & 0xff, ref_u[i]); | |
| 481 else | |
| 482 assertClose(1, buffer.get() & 0xff, ref_v[i]); | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 surfaceTextureHelper.dispose(); | |
| 487 eglBase.release(); | |
| 488 } | |
| 489 } | |
| OLD | NEW |