This migrates all tests that work by just changing their runner. This excludes tests using `@RunWith(ParameterizedRunner.class)`, and a few other non-parameterized tests that fail with the default runner. Bug: webrtc:13662 Change-Id: Ia0b7c80e04a6a6b7a51348b3a7f587d10061b58e Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/256367 Reviewed-by: Björn Terelius <terelius@webrtc.org> Commit-Queue: Xavier Lepaul <xalep@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36298}
519 lines
21 KiB
Java
519 lines
21 KiB
Java
/*
|
|
* Copyright 2015 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
package org.webrtc;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
import android.opengl.GLES20;
|
|
import android.os.SystemClock;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.test.filters.MediumTest;
|
|
import androidx.test.filters.SmallTest;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
|
|
public class SurfaceTextureHelperTest {
|
|
/**
|
|
* Mock texture listener with blocking wait functionality.
|
|
*/
|
|
public static final class MockTextureListener implements VideoSink {
|
|
private final Object lock = new Object();
|
|
private @Nullable VideoFrame.TextureBuffer textureBuffer;
|
|
// Thread where frames are expected to be received on.
|
|
private final @Nullable Thread expectedThread;
|
|
|
|
MockTextureListener() {
|
|
this.expectedThread = null;
|
|
}
|
|
|
|
MockTextureListener(Thread expectedThread) {
|
|
this.expectedThread = expectedThread;
|
|
}
|
|
|
|
@Override
|
|
public void onFrame(VideoFrame frame) {
|
|
if (expectedThread != null && Thread.currentThread() != expectedThread) {
|
|
throw new IllegalStateException("onTextureFrameAvailable called on wrong thread.");
|
|
}
|
|
synchronized (lock) {
|
|
this.textureBuffer = (VideoFrame.TextureBuffer) frame.getBuffer();
|
|
textureBuffer.retain();
|
|
lock.notifyAll();
|
|
}
|
|
}
|
|
|
|
/** Wait indefinitely for a new textureBuffer. */
|
|
public VideoFrame.TextureBuffer waitForTextureBuffer() throws InterruptedException {
|
|
synchronized (lock) {
|
|
while (true) {
|
|
final VideoFrame.TextureBuffer textureBufferToReturn = textureBuffer;
|
|
if (textureBufferToReturn != null) {
|
|
textureBuffer = null;
|
|
return textureBufferToReturn;
|
|
}
|
|
lock.wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Make sure we get no frame in the specified time period. */
|
|
public void assertNoFrameIsDelivered(final long waitPeriodMs) throws InterruptedException {
|
|
final long startTimeMs = SystemClock.elapsedRealtime();
|
|
long timeRemainingMs = waitPeriodMs;
|
|
synchronized (lock) {
|
|
while (textureBuffer == null && timeRemainingMs > 0) {
|
|
lock.wait(timeRemainingMs);
|
|
final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs;
|
|
timeRemainingMs = waitPeriodMs - elapsedTimeMs;
|
|
}
|
|
assertTrue(textureBuffer == null);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Assert that two integers are close, with difference at most
|
|
* {@code threshold}. */
|
|
public static void assertClose(int threshold, int expected, int actual) {
|
|
if (Math.abs(expected - actual) <= threshold)
|
|
return;
|
|
fail("Not close enough, threshold " + threshold + ". Expected: " + expected + " Actual: "
|
|
+ actual);
|
|
}
|
|
|
|
@Before
|
|
public void setUp() {
|
|
// Load the JNI library for textureToYuv.
|
|
NativeLibrary.initialize(new NativeLibrary.DefaultLoader(), TestConstants.NATIVE_LIBRARY);
|
|
}
|
|
|
|
/**
|
|
* Test normal use by receiving three uniform texture frames. Texture frames are returned as early
|
|
* as possible. The texture pixel values are inspected by drawing the texture frame to a pixel
|
|
* buffer and reading it back with glReadPixels().
|
|
*/
|
|
@Test
|
|
@MediumTest
|
|
public void testThreeConstantColorFrames() throws InterruptedException {
|
|
final int width = 16;
|
|
final int height = 16;
|
|
// Create EGL base with a pixel buffer as display output.
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
|
|
eglBase.createPbufferSurface(width, height);
|
|
final GlRectDrawer drawer = new GlRectDrawer();
|
|
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(
|
|
"SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext());
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
surfaceTextureHelper.setTextureSize(width, height);
|
|
|
|
// Create resources for stubbing an OES texture producer. `eglOesBase` has the SurfaceTexture in
|
|
// `surfaceTextureHelper` as the target EGLSurface.
|
|
final EglBase eglOesBase = EglBase.create(eglBase.getEglBaseContext(), EglBase.CONFIG_PLAIN);
|
|
eglOesBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
assertEquals(eglOesBase.surfaceWidth(), width);
|
|
assertEquals(eglOesBase.surfaceHeight(), height);
|
|
|
|
final int red[] = new int[] {79, 144, 185};
|
|
final int green[] = new int[] {66, 210, 162};
|
|
final int blue[] = new int[] {161, 117, 158};
|
|
// Draw three frames.
|
|
for (int i = 0; i < 3; ++i) {
|
|
// Draw a constant color frame onto the SurfaceTexture.
|
|
eglOesBase.makeCurrent();
|
|
GLES20.glClearColor(red[i] / 255.0f, green[i] / 255.0f, blue[i] / 255.0f, 1.0f);
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglOesBase.swapBuffers();
|
|
|
|
// Wait for an OES texture to arrive and draw it onto the pixel buffer.
|
|
final VideoFrame.TextureBuffer textureBuffer = listener.waitForTextureBuffer();
|
|
eglBase.makeCurrent();
|
|
drawer.drawOes(textureBuffer.getTextureId(),
|
|
RendererCommon.convertMatrixFromAndroidGraphicsMatrix(textureBuffer.getTransformMatrix()),
|
|
width, height, 0, 0, width, height);
|
|
textureBuffer.release();
|
|
|
|
// Download the pixels in the pixel buffer as RGBA. Not all platforms support RGB, e.g.
|
|
// Nexus 9.
|
|
final ByteBuffer rgbaData = ByteBuffer.allocateDirect(width * height * 4);
|
|
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaData);
|
|
GlUtil.checkNoGLES2Error("glReadPixels");
|
|
|
|
// Assert rendered image is expected constant color.
|
|
while (rgbaData.hasRemaining()) {
|
|
assertEquals(rgbaData.get() & 0xFF, red[i]);
|
|
assertEquals(rgbaData.get() & 0xFF, green[i]);
|
|
assertEquals(rgbaData.get() & 0xFF, blue[i]);
|
|
assertEquals(rgbaData.get() & 0xFF, 255);
|
|
}
|
|
}
|
|
|
|
drawer.release();
|
|
surfaceTextureHelper.dispose();
|
|
eglBase.release();
|
|
}
|
|
|
|
/**
|
|
* Test disposing the SurfaceTextureHelper while holding a pending texture frame. The pending
|
|
* texture frame should still be valid, and this is tested by drawing the texture frame to a pixel
|
|
* buffer and reading it back with glReadPixels().
|
|
*/
|
|
@Test
|
|
@MediumTest
|
|
public void testLateReturnFrame() throws InterruptedException {
|
|
final int width = 16;
|
|
final int height = 16;
|
|
// Create EGL base with a pixel buffer as display output.
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
|
|
eglBase.createPbufferSurface(width, height);
|
|
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(
|
|
"SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext());
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
surfaceTextureHelper.setTextureSize(width, height);
|
|
|
|
// Create resources for stubbing an OES texture producer. `eglOesBase` has the SurfaceTexture in
|
|
// `surfaceTextureHelper` as the target EGLSurface.
|
|
final EglBase eglOesBase = EglBase.create(eglBase.getEglBaseContext(), EglBase.CONFIG_PLAIN);
|
|
eglOesBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
assertEquals(eglOesBase.surfaceWidth(), width);
|
|
assertEquals(eglOesBase.surfaceHeight(), height);
|
|
|
|
final int red = 79;
|
|
final int green = 66;
|
|
final int blue = 161;
|
|
// Draw a constant color frame onto the SurfaceTexture.
|
|
eglOesBase.makeCurrent();
|
|
GLES20.glClearColor(red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f);
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglOesBase.swapBuffers();
|
|
eglOesBase.release();
|
|
|
|
// Wait for OES texture frame.
|
|
final VideoFrame.TextureBuffer textureBuffer = listener.waitForTextureBuffer();
|
|
// Diconnect while holding the frame.
|
|
surfaceTextureHelper.dispose();
|
|
|
|
// Draw the pending texture frame onto the pixel buffer.
|
|
eglBase.makeCurrent();
|
|
final GlRectDrawer drawer = new GlRectDrawer();
|
|
drawer.drawOes(textureBuffer.getTextureId(),
|
|
RendererCommon.convertMatrixFromAndroidGraphicsMatrix(textureBuffer.getTransformMatrix()),
|
|
width, height, 0, 0, width, height);
|
|
drawer.release();
|
|
|
|
// Download the pixels in the pixel buffer as RGBA. Not all platforms support RGB, e.g. Nexus 9.
|
|
final ByteBuffer rgbaData = ByteBuffer.allocateDirect(width * height * 4);
|
|
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaData);
|
|
GlUtil.checkNoGLES2Error("glReadPixels");
|
|
eglBase.release();
|
|
|
|
// Assert rendered image is expected constant color.
|
|
while (rgbaData.hasRemaining()) {
|
|
assertEquals(rgbaData.get() & 0xFF, red);
|
|
assertEquals(rgbaData.get() & 0xFF, green);
|
|
assertEquals(rgbaData.get() & 0xFF, blue);
|
|
assertEquals(rgbaData.get() & 0xFF, 255);
|
|
}
|
|
// Late frame return after everything has been disposed and released.
|
|
textureBuffer.release();
|
|
}
|
|
|
|
/**
|
|
* Test disposing the SurfaceTextureHelper, but keep trying to produce more texture frames. No
|
|
* frames should be delivered to the listener.
|
|
*/
|
|
@Test
|
|
@MediumTest
|
|
public void testDispose() throws InterruptedException {
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
// Create EglBase with the SurfaceTexture as target EGLSurface.
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN);
|
|
surfaceTextureHelper.setTextureSize(/* textureWidth= */ 32, /* textureHeight= */ 32);
|
|
eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
eglBase.makeCurrent();
|
|
// Assert no frame has been received yet.
|
|
listener.assertNoFrameIsDelivered(/* waitPeriodMs= */ 1);
|
|
// Draw and wait for one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglBase.swapBuffers();
|
|
listener.waitForTextureBuffer().release();
|
|
|
|
// Dispose - we should not receive any textures after this.
|
|
surfaceTextureHelper.dispose();
|
|
|
|
// Draw one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
eglBase.swapBuffers();
|
|
// swapBuffers() should not trigger onTextureFrameAvailable() because disposed has been called.
|
|
// Assert that no OES texture was delivered.
|
|
listener.assertNoFrameIsDelivered(/* waitPeriodMs= */ 500);
|
|
|
|
eglBase.release();
|
|
}
|
|
|
|
/**
|
|
* Test disposing the SurfaceTextureHelper immediately after is has been setup to use a
|
|
* shared context. No frames should be delivered to the listener.
|
|
*/
|
|
@Test
|
|
@SmallTest
|
|
public void testDisposeImmediately() {
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
surfaceTextureHelper.dispose();
|
|
}
|
|
|
|
/**
|
|
* Call stopListening(), but keep trying to produce more texture frames. No frames should be
|
|
* delivered to the listener.
|
|
*/
|
|
@Test
|
|
@MediumTest
|
|
public void testStopListening() throws InterruptedException {
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
surfaceTextureHelper.setTextureSize(/* textureWidth= */ 32, /* textureHeight= */ 32);
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
// Create EglBase with the SurfaceTexture as target EGLSurface.
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN);
|
|
eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
eglBase.makeCurrent();
|
|
// Assert no frame has been received yet.
|
|
listener.assertNoFrameIsDelivered(/* waitPeriodMs= */ 1);
|
|
// Draw and wait for one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglBase.swapBuffers();
|
|
listener.waitForTextureBuffer().release();
|
|
|
|
// Stop listening - we should not receive any textures after this.
|
|
surfaceTextureHelper.stopListening();
|
|
|
|
// Draw one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
eglBase.swapBuffers();
|
|
// swapBuffers() should not trigger onTextureFrameAvailable() because disposed has been called.
|
|
// Assert that no OES texture was delivered.
|
|
listener.assertNoFrameIsDelivered(/* waitPeriodMs= */ 500);
|
|
|
|
surfaceTextureHelper.dispose();
|
|
eglBase.release();
|
|
}
|
|
|
|
/**
|
|
* Test stopListening() immediately after the SurfaceTextureHelper has been setup.
|
|
*/
|
|
@Test
|
|
@SmallTest
|
|
public void testStopListeningImmediately() throws InterruptedException {
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
surfaceTextureHelper.stopListening();
|
|
surfaceTextureHelper.dispose();
|
|
}
|
|
|
|
/**
|
|
* Test stopListening() immediately after the SurfaceTextureHelper has been setup on the handler
|
|
* thread.
|
|
*/
|
|
@Test
|
|
@SmallTest
|
|
public void testStopListeningImmediatelyOnHandlerThread() throws InterruptedException {
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
|
|
final CountDownLatch stopListeningBarrier = new CountDownLatch(1);
|
|
final CountDownLatch stopListeningBarrierDone = new CountDownLatch(1);
|
|
// Start by posting to the handler thread to keep it occupied.
|
|
surfaceTextureHelper.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
ThreadUtils.awaitUninterruptibly(stopListeningBarrier);
|
|
surfaceTextureHelper.stopListening();
|
|
stopListeningBarrierDone.countDown();
|
|
}
|
|
});
|
|
|
|
// startListening() is asynchronous and will post to the occupied handler thread.
|
|
surfaceTextureHelper.startListening(listener);
|
|
// Wait for stopListening() to be called on the handler thread.
|
|
stopListeningBarrier.countDown();
|
|
stopListeningBarrierDone.await();
|
|
// Wait until handler thread is idle to try to catch late startListening() call.
|
|
final CountDownLatch barrier = new CountDownLatch(1);
|
|
surfaceTextureHelper.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
barrier.countDown();
|
|
}
|
|
});
|
|
ThreadUtils.awaitUninterruptibly(barrier);
|
|
// Previous startListening() call should never have taken place and it should be ok to call it
|
|
// again.
|
|
surfaceTextureHelper.startListening(listener);
|
|
|
|
surfaceTextureHelper.dispose();
|
|
}
|
|
|
|
/**
|
|
* Test calling startListening() with a new listener after stopListening() has been called.
|
|
*/
|
|
@Test
|
|
@MediumTest
|
|
public void testRestartListeningWithNewListener() throws InterruptedException {
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper =
|
|
SurfaceTextureHelper.create("SurfaceTextureHelper test" /* threadName */, null);
|
|
surfaceTextureHelper.setTextureSize(/* textureWidth= */ 32, /* textureHeight= */ 32);
|
|
final MockTextureListener listener1 = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener1);
|
|
// Create EglBase with the SurfaceTexture as target EGLSurface.
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN);
|
|
eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
eglBase.makeCurrent();
|
|
// Assert no frame has been received yet.
|
|
listener1.assertNoFrameIsDelivered(/* waitPeriodMs= */ 1);
|
|
// Draw and wait for one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglBase.swapBuffers();
|
|
listener1.waitForTextureBuffer().release();
|
|
|
|
// Stop listening - `listener1` should not receive any textures after this.
|
|
surfaceTextureHelper.stopListening();
|
|
|
|
// Connect different listener.
|
|
final MockTextureListener listener2 = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener2);
|
|
// Assert no frame has been received yet.
|
|
listener2.assertNoFrameIsDelivered(/* waitPeriodMs= */ 1);
|
|
|
|
// Draw one frame.
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
eglBase.swapBuffers();
|
|
|
|
// Check that `listener2` received the frame, and not `listener1`.
|
|
listener2.waitForTextureBuffer().release();
|
|
listener1.assertNoFrameIsDelivered(/* waitPeriodMs= */ 1);
|
|
|
|
surfaceTextureHelper.dispose();
|
|
eglBase.release();
|
|
}
|
|
|
|
@Test
|
|
@MediumTest
|
|
public void testTexturetoYuv() throws InterruptedException {
|
|
final int width = 16;
|
|
final int height = 16;
|
|
|
|
final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN);
|
|
|
|
// Create SurfaceTextureHelper and listener.
|
|
final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(
|
|
"SurfaceTextureHelper test" /* threadName */, eglBase.getEglBaseContext());
|
|
final MockTextureListener listener = new MockTextureListener();
|
|
surfaceTextureHelper.startListening(listener);
|
|
surfaceTextureHelper.setTextureSize(width, height);
|
|
|
|
// Create resources for stubbing an OES texture producer. `eglBase` has the SurfaceTexture in
|
|
// `surfaceTextureHelper` as the target EGLSurface.
|
|
|
|
eglBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
|
assertEquals(eglBase.surfaceWidth(), width);
|
|
assertEquals(eglBase.surfaceHeight(), height);
|
|
|
|
final int red[] = new int[] {79, 144, 185};
|
|
final int green[] = new int[] {66, 210, 162};
|
|
final int blue[] = new int[] {161, 117, 158};
|
|
|
|
final int ref_y[] = new int[] {85, 170, 161};
|
|
final int ref_u[] = new int[] {168, 97, 123};
|
|
final int ref_v[] = new int[] {127, 106, 138};
|
|
|
|
// Draw three frames.
|
|
for (int i = 0; i < 3; ++i) {
|
|
// Draw a constant color frame onto the SurfaceTexture.
|
|
eglBase.makeCurrent();
|
|
GLES20.glClearColor(red[i] / 255.0f, green[i] / 255.0f, blue[i] / 255.0f, 1.0f);
|
|
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
|
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
|
eglBase.swapBuffers();
|
|
|
|
// Wait for an OES texture to arrive.
|
|
final VideoFrame.TextureBuffer textureBuffer = listener.waitForTextureBuffer();
|
|
final VideoFrame.I420Buffer i420 = textureBuffer.toI420();
|
|
textureBuffer.release();
|
|
|
|
// Memory layout: Lines are 16 bytes. First 16 lines are
|
|
// the Y data. These are followed by 8 lines with 8 bytes of U
|
|
// data on the left and 8 bytes of V data on the right.
|
|
//
|
|
// Offset
|
|
// 0 YYYYYYYY YYYYYYYY
|
|
// 16 YYYYYYYY YYYYYYYY
|
|
// ...
|
|
// 240 YYYYYYYY YYYYYYYY
|
|
// 256 UUUUUUUU VVVVVVVV
|
|
// 272 UUUUUUUU VVVVVVVV
|
|
// ...
|
|
// 368 UUUUUUUU VVVVVVVV
|
|
// 384 buffer end
|
|
|
|
// Allow off-by-one differences due to different rounding.
|
|
final ByteBuffer dataY = i420.getDataY();
|
|
final int strideY = i420.getStrideY();
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
assertClose(1, ref_y[i], dataY.get(y * strideY + x) & 0xFF);
|
|
}
|
|
}
|
|
|
|
final int chromaWidth = width / 2;
|
|
final int chromaHeight = height / 2;
|
|
|
|
final ByteBuffer dataU = i420.getDataU();
|
|
final ByteBuffer dataV = i420.getDataV();
|
|
final int strideU = i420.getStrideU();
|
|
final int strideV = i420.getStrideV();
|
|
for (int y = 0; y < chromaHeight; y++) {
|
|
for (int x = 0; x < chromaWidth; x++) {
|
|
assertClose(1, ref_u[i], dataU.get(y * strideU + x) & 0xFF);
|
|
assertClose(1, ref_v[i], dataV.get(y * strideV + x) & 0xFF);
|
|
}
|
|
}
|
|
i420.release();
|
|
}
|
|
|
|
surfaceTextureHelper.dispose();
|
|
eglBase.release();
|
|
}
|
|
}
|