Android SurfaceTextureHelper: Don't wait for pending frames in disconnect()

This CL also makes some small non-functional changes in ThreadUtils and EglBase to support SurfaceTextures and SurfaceTextureHelper.

BUG=webrtc:4993
R=hbos@webrtc.org

Review URL: https://codereview.webrtc.org/1368093003 .

Cr-Commit-Position: refs/heads/master@{#10085}
This commit is contained in:
Magnus Jedvert
2015-09-28 11:05:44 +02:00
parent 3e9eb4ba01
commit 1ab271c1c4
3 changed files with 58 additions and 31 deletions

View File

@ -27,6 +27,7 @@
package org.webrtc; package org.webrtc;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14; import android.opengl.EGL14;
import android.opengl.EGLConfig; import android.opengl.EGLConfig;
import android.opengl.EGLContext; import android.opengl.EGLContext;
@ -85,6 +86,19 @@ public final class EglBase {
// Create EGLSurface from the Android Surface. // Create EGLSurface from the Android Surface.
public void createSurface(Surface surface) { public void createSurface(Surface surface) {
createSurfaceInternal(surface);
}
// Create EGLSurface from the Android SurfaceTexture.
public void createSurface(SurfaceTexture surfaceTexture) {
createSurfaceInternal(surfaceTexture);
}
// Create EGLSurface from either Surface or SurfaceTexture.
private void createSurfaceInternal(Object surface) {
if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
throw new IllegalStateException("Input must be either a Surface or SurfaceTexture");
}
checkIsNotReleased(); checkIsNotReleased();
if (configType == ConfigType.PIXEL_BUFFER) { if (configType == ConfigType.PIXEL_BUFFER) {
Logging.w(TAG, "This EGL context is configured for PIXEL_BUFFER, but uses regular Surface"); Logging.w(TAG, "This EGL context is configured for PIXEL_BUFFER, but uses regular Surface");

View File

@ -35,7 +35,6 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -159,20 +158,7 @@ final class SurfaceTextureHelper {
} }
} }
}); });
boolean wasInterrupted = true; ThreadUtils.awaitUninterruptibly(barrier);
while(true) {
try {
barrier.await();
break;
} catch (InterruptedException e) {
// Someone is asking us to return early at our convenience. We must wait until the
// |isQuitting| flag has been set but we should preserve the information and pass it along.
wasInterrupted = true;
}
}
if (wasInterrupted) {
Thread.currentThread().interrupt();
}
} }
private void tryDeliverTextureFrame() { private void tryDeliverTextureFrame() {
@ -195,18 +181,15 @@ final class SurfaceTextureHelper {
} }
private void release() { private void release() {
if (Thread.currentThread() != thread) {
throw new IllegalStateException("Wrong thread.");
}
if (isTextureInUse || !isQuitting) { if (isTextureInUse || !isQuitting) {
throw new IllegalStateException("Unexpected release."); throw new IllegalStateException("Unexpected release.");
} }
// Release GL resources on dedicated thread.
handler.post(new Runnable() {
@Override public void run() {
GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0); GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0);
surfaceTexture.release(); surfaceTexture.release();
eglBase.release(); eglBase.release();
}
});
// Quit safely to make sure the clean-up posted above is executed.
thread.quitSafely(); thread.quitSafely();
} }
} }

View File

@ -27,20 +27,32 @@
package org.webrtc; package org.webrtc;
public class ThreadUtils { import java.util.concurrent.CountDownLatch;
final class ThreadUtils {
/** /**
* Helper function to make sure a thread is joined without getting interrupted. This should be * Utility interface to be used with executeUninterruptibly() to wait for blocking operations
* used in cases where |thread| is doing some critical work, e.g. cleanup, that must complete * to complete without getting interrupted..
* before returning. The thread interruption flag is set if an interrupt occurs during join().
*/ */
public static void joinUninterruptibly(Thread thread) { public interface BlockingOperation {
void run() throws InterruptedException;
}
/**
* Utility method to make sure a blocking operation is executed to completion without getting
* interrupted. This should be used in cases where the operation is waiting for some critical
* work, e.g. cleanup, that must complete before returning. If the thread is interrupted during
* the blocking operation, this function will re-run the operation until completion, and only then
* re-interrupt the thread.
*/
public static void executeUninterruptibly(BlockingOperation operation) {
boolean wasInterrupted = false; boolean wasInterrupted = false;
while (true) { while (true) {
try { try {
thread.join(); operation.run();
break; break;
} catch (InterruptedException e) { } catch (InterruptedException e) {
// Someone is asking us to return early at our convenience. We can't cancel this join(), // Someone is asking us to return early at our convenience. We can't cancel this operation,
// but we should preserve the information and pass it along. // but we should preserve the information and pass it along.
wasInterrupted = true; wasInterrupted = true;
} }
@ -50,4 +62,22 @@ public class ThreadUtils {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} }
public static void joinUninterruptibly(final Thread thread) {
executeUninterruptibly(new BlockingOperation() {
@Override
public void run() throws InterruptedException {
thread.join();
}
});
}
public static void awaitUninterruptibly(final CountDownLatch latch) {
executeUninterruptibly(new BlockingOperation() {
@Override
public void run() throws InterruptedException {
latch.await();
}
});
}
} }