Android SurfaceViewRenderer: Block in release() until frames are returned and cleanup is done

BUG=webrtc:4742,webrtc:4909
R=hbos@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#10000}
This commit is contained in:
Magnus Jedvert
2015-09-21 14:32:07 +02:00
parent e6d3adab08
commit c19922c181

View File

@ -54,11 +54,11 @@ public class SurfaceViewRenderer extends SurfaceView
implements SurfaceHolder.Callback, VideoRenderer.Callbacks {
private static final String TAG = "SurfaceViewRenderer";
// These variables are synchronized on |threadLock|.
private final Object threadLock = new Object();
// Dedicated render thread.
private HandlerThread renderThread;
// Handler for inter-thread communication.
// |renderThreadHandler| is a handler for communicating with |renderThread|, and is synchronized
// on |handlerLock|.
private final Object handlerLock = new Object();
private Handler renderThreadHandler;
// EGL and GL resources for drawing YUV/OES textures. After initilization, these are only accessed
@ -156,11 +156,13 @@ public class SurfaceViewRenderer extends SurfaceView
}
/**
* Release all resources. This needs to be done manually, otherwise the resources are leaked. You
* should call this before the Activity is destroyed, while the EGLContext is still valid.
* Block until any pending frame is returned and all GL resources released, even if an interrupt
* occurs. If an interrupt occurs during release(), the interrupt flag will be set. This function
* should be called before the Activity is destroyed and the EGLContext is still valid. If you
* don't call this function, the GL resources might leak.
*/
public void release() {
synchronized (threadLock) {
synchronized (handlerLock) {
if (renderThreadHandler == null) {
Logging.d(TAG, "Already released");
return;
@ -169,7 +171,7 @@ public class SurfaceViewRenderer extends SurfaceView
// TODO(magjed): This might not be necessary - all OpenGL resources are automatically deleted
// when the EGL context is lost. It might be dangerous to delete them manually in
// Activity.onDestroy().
renderThreadHandler.post(new Runnable() {
renderThreadHandler.postAtFrontOfQueue(new Runnable() {
@Override public void run() {
drawer.release();
drawer = null;
@ -181,19 +183,35 @@ public class SurfaceViewRenderer extends SurfaceView
eglBase = null;
}
});
// Don't accept any more messages to the render thread.
// Don't accept any more frames or messages to the render thread.
renderThreadHandler = null;
// Quit safely to make sure the EGL/GL cleanup posted above is executed.
renderThread.quitSafely();
renderThread = null;
}
getHolder().removeCallback(this);
// Quit safely to make sure the EGL/GL cleanup posted above is executed.
renderThread.quitSafely();
synchronized (frameLock) {
if (pendingFrame != null) {
VideoRenderer.renderFrameDone(pendingFrame);
pendingFrame = null;
}
}
// The |renderThread| cleanup is not safe to cancel and we need to wait until it's done.
boolean wasInterrupted = false;
while (true) {
try {
renderThread.join();
renderThread = null;
break;
} catch (InterruptedException e) {
// Someone is asking us to return early at our convenience. We can't cancel this cleanup,
// but we should preserve the information and pass it along. Any rendering in progress
// should complete in a few ms.
wasInterrupted = true;
}
}
// Pass interruption information along.
if (wasInterrupted) {
Thread.currentThread().interrupt();
}
}
/**
@ -220,7 +238,7 @@ public class SurfaceViewRenderer extends SurfaceView
synchronized (statisticsLock) {
++framesReceived;
}
synchronized (threadLock) {
synchronized (handlerLock) {
if (renderThreadHandler == null) {
Logging.d(TAG, "Dropping frame - SurfaceViewRenderer not initialized or already released.");
} else {
@ -322,7 +340,7 @@ public class SurfaceViewRenderer extends SurfaceView
* Private helper function to post tasks safely.
*/
private void runOnRenderThread(Runnable runnable) {
synchronized (threadLock) {
synchronized (handlerLock) {
if (renderThreadHandler != null) {
renderThreadHandler.post(runnable);
}