From a7409d87247162002fca1719c035de67fa7e7535 Mon Sep 17 00:00:00 2001
From: Eino-Ville Talvala <etalvala@google.com>
Date: Tue, 20 Sep 2011 13:49:42 -0700
Subject: Effects: Fix SurfaceTexture abandon/disconnect errors.

- Now ensures that camera preview is stopped before starting or
stopping the effects processing, and that the previewTexture
is set to null.
- Fix a few potential race conditions relating to effect switching
- Tear down old graphs properly

This fixes errors relating to SurfaceTexture disconnect/abandon errors
when swapping between effects, and possibly some rarely-occuring race
condition crashes.

Bug: 5328760
Change-Id: I6b655f32b835e7ac65cb7c8dc533befb5177907c
---
 src/com/android/camera/EffectsRecorder.java | 47 +++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 12 deletions(-)

(limited to 'src/com')

diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java
index f78f5dc..6d4faf4 100644
--- a/src/com/android/camera/EffectsRecorder.java
+++ b/src/com/android/camera/EffectsRecorder.java
@@ -85,6 +85,7 @@ public class EffectsRecorder {
     private GraphEnvironment mGraphEnv;
     private int mGraphId;
     private GraphRunner mRunner = null;
+    private GraphRunner mOldRunner = null;
 
     private SurfaceTexture mTextureSource;
 
@@ -262,7 +263,7 @@ public class EffectsRecorder {
         mErrorListener = errorListener;
     }
 
-    public void initializeFilterFramework() {
+    private void initializeFilterFramework() {
         mGraphEnv = new GraphEnvironment();
         mGraphEnv.createGLEnvironment();
 
@@ -279,7 +280,7 @@ public class EffectsRecorder {
         mCurrentEffect = EFFECT_NONE;
     }
 
-    public synchronized void initializeEffect(boolean forceReset) {
+    private synchronized void initializeEffect(boolean forceReset) {
         if (forceReset ||
             mCurrentEffect != mEffect ||
             mCurrentEffect == EFFECT_BACKDROPPER) {
@@ -287,13 +288,11 @@ public class EffectsRecorder {
                     "previewSurface", mPreviewSurfaceHolder.getSurface(),
                     "previewWidth", mPreviewWidth,
                     "previewHeight", mPreviewHeight);
-
             if (mState == STATE_PREVIEW) {
-                // Switching effects while running. Stop existing runner.
-                // The stop callback will take care of starting new runner.
+                // Switching effects while running. Inform video camera.
                 sendMessage(mCurrentEffect, EFFECT_MSG_STOPPING_EFFECT);
-                mRunner.stop();
             }
+
             switch (mEffect) {
                 case EFFECT_GOOFY_FACE:
                     mGraphId = mGraphEnv.loadGraph(mContext, R.raw.goofy_face);
@@ -307,8 +306,21 @@ public class EffectsRecorder {
             }
             mCurrentEffect = mEffect;
 
+            mOldRunner = mRunner;
             mRunner = mGraphEnv.getRunner(mGraphId, GraphEnvironment.MODE_ASYNCHRONOUS);
             mRunner.setDoneCallback(mRunnerDoneCallback);
+
+            if (mState == STATE_PREVIEW) {
+                // Switching effects while running. Stop existing runner.
+                // The stop callback will take care of starting new runner.
+                mCameraDevice.stopPreview();
+                try {
+                    mCameraDevice.setPreviewTexture(null);
+                } catch(IOException e) {
+                    throw new RuntimeException("Unable to connect camera to effect input", e);
+                }
+                mOldRunner.stop();
+            }
         }
 
         switch (mCurrentEffect) {
@@ -328,7 +340,7 @@ public class EffectsRecorder {
         setFaceDetectOrientation(mOrientationHint);
     }
 
-    public void startPreview() {
+    public synchronized void startPreview() {
         if (mLogVerbose) Log.v(TAG, "Starting preview (" + this + ")");
 
         switch (mState) {
@@ -421,7 +433,7 @@ public class EffectsRecorder {
         }
     };
 
-    public void startRecording() {
+    public synchronized void startRecording() {
         if (mLogVerbose) Log.v(TAG, "Starting recording (" + this + ")");
 
         switch (mState) {
@@ -453,7 +465,7 @@ public class EffectsRecorder {
         mState = STATE_RECORD;
     }
 
-    public void stopRecording() {
+    public synchronized void stopRecording() {
         if (mLogVerbose) Log.v(TAG, "Stop recording (" + this + ")");
 
         switch (mState) {
@@ -472,7 +484,7 @@ public class EffectsRecorder {
     }
 
     // Stop and release effect resources
-    public void stopPreview() {
+    public synchronized void stopPreview() {
         if (mLogVerbose) Log.v(TAG, "Stopping preview (" + this + ")");
 
         switch (mState) {
@@ -493,7 +505,15 @@ public class EffectsRecorder {
 
         mCurrentEffect = EFFECT_NONE;
 
+        mCameraDevice.stopPreview();
+        try {
+            mCameraDevice.setPreviewTexture(null);
+        } catch(IOException e) {
+            throw new RuntimeException("Unable to connect camera to effect input", e);
+        }
+
         mState = STATE_CONFIGURE;
+        mOldRunner = mRunner;
         mRunner.stop();
 
         // Rest of stop and release handled in mRunnerDoneCallback
@@ -525,6 +545,11 @@ public class EffectsRecorder {
             new OnRunnerDoneListener() {
         public void onRunnerDone(int result) {
             synchronized(EffectsRecorder.this) {
+                if (mOldRunner != null) {
+                    if (mLogVerbose) Log.v(TAG, "Tearing down old graph.");
+                    mOldRunner.getGraph().tearDown(mGraphEnv.getContext());
+                    mOldRunner = null;
+                }
                 if (mState == STATE_PREVIEW) {
                     // Switching effects, start up the new runner
                     if (mLogVerbose) Log.v(TAG, "Previous effect halted, starting new effect.");
@@ -533,7 +558,6 @@ public class EffectsRecorder {
                 } else if (mState != STATE_RELEASED) {
                     // Shutting down effects
                     if (mLogVerbose) Log.v(TAG, "Runner halted, restoring direct preview");
-                    mCameraDevice.stopPreview();
                     try {
                         mCameraDevice.setPreviewDisplay(mPreviewSurfaceHolder);
                     } catch(IOException e) {
@@ -555,7 +579,6 @@ public class EffectsRecorder {
             case STATE_RECORD:
             case STATE_PREVIEW:
                 stopPreview();
-                mCameraDevice.stopPreview();
                 // Fall-through
             default:
                 mState = STATE_RELEASED;
-- 
cgit v1.1