summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/EffectsRecorder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/EffectsRecorder.java')
-rw-r--r--src/com/android/camera/EffectsRecorder.java1240
1 files changed, 0 insertions, 1240 deletions
diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java
deleted file mode 100644
index 151441ef9..000000000
--- a/src/com/android/camera/EffectsRecorder.java
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.camera;
-
-import java.io.FileDescriptor;
-import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.media.CamcorderProfile;
-import android.media.MediaRecorder;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-
-import com.android.camera.util.ApiHelper;
-import com.android.camera.util.CameraUtil;
-import com.android.camera2.R;
-
-
-/**
- * Encapsulates the mobile filter framework components needed to record video
- * with effects applied. Modeled after MediaRecorder.
- */
-@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) // uses SurfaceTexture
-public class EffectsRecorder {
- private static final String TAG = "EffectsRecorder";
-
- private static Class<?> sClassFilter;
- private static Method sFilterIsAvailable;
- private static EffectsRecorder sEffectsRecorder;
- // The index of the current effects recorder.
- private static int sEffectsRecorderIndex;
-
- private static boolean sReflectionInited = false;
-
- private static Class<?> sClsLearningDoneListener;
- private static Class<?> sClsOnRunnerDoneListener;
- private static Class<?> sClsOnRecordingDoneListener;
- private static Class<?> sClsSurfaceTextureSourceListener;
-
- private static Method sFilterSetInputValue;
-
- private static Constructor<?> sCtPoint;
- private static Constructor<?> sCtQuad;
-
- private static Method sLearningDoneListenerOnLearningDone;
-
- private static Method sObjectEquals;
- private static Method sObjectToString;
-
- private static Class<?> sClsGraphRunner;
- private static Method sGraphRunnerGetGraph;
- private static Method sGraphRunnerSetDoneCallback;
- private static Method sGraphRunnerRun;
- private static Method sGraphRunnerGetError;
- private static Method sGraphRunnerStop;
-
- private static Method sFilterGraphGetFilter;
- private static Method sFilterGraphTearDown;
-
- private static Method sOnRunnerDoneListenerOnRunnerDone;
-
- private static Class<?> sClsGraphEnvironment;
- private static Constructor<?> sCtGraphEnvironment;
- private static Method sGraphEnvironmentCreateGLEnvironment;
- private static Method sGraphEnvironmentGetRunner;
- private static Method sGraphEnvironmentAddReferences;
- private static Method sGraphEnvironmentLoadGraph;
- private static Method sGraphEnvironmentGetContext;
-
- private static Method sFilterContextGetGLEnvironment;
- private static Method sGLEnvironmentIsActive;
- private static Method sGLEnvironmentActivate;
- private static Method sGLEnvironmentDeactivate;
- private static Method sSurfaceTextureTargetDisconnect;
- private static Method sOnRecordingDoneListenerOnRecordingDone;
- private static Method sSurfaceTextureSourceListenerOnSurfaceTextureSourceReady;
-
- private Object mLearningDoneListener;
- private Object mRunnerDoneCallback;
- private Object mSourceReadyCallback;
- // A callback to finalize the media after the recording is done.
- private Object mRecordingDoneListener;
-
- static {
- try {
- sClassFilter = Class.forName("android.filterfw.core.Filter");
- sFilterIsAvailable = sClassFilter.getMethod("isAvailable",
- String.class);
- } catch (ClassNotFoundException ex) {
- Log.v(TAG, "Can't find the class android.filterfw.core.Filter");
- } catch (NoSuchMethodException e) {
- Log.v(TAG, "Can't find the method Filter.isAvailable");
- }
- }
-
- public static final int EFFECT_NONE = 0;
- public static final int EFFECT_GOOFY_FACE = 1;
- public static final int EFFECT_BACKDROPPER = 2;
-
- public static final int EFFECT_GF_SQUEEZE = 0;
- public static final int EFFECT_GF_BIG_EYES = 1;
- public static final int EFFECT_GF_BIG_MOUTH = 2;
- public static final int EFFECT_GF_SMALL_MOUTH = 3;
- public static final int EFFECT_GF_BIG_NOSE = 4;
- public static final int EFFECT_GF_SMALL_EYES = 5;
- public static final int NUM_OF_GF_EFFECTS = EFFECT_GF_SMALL_EYES + 1;
-
- public static final int EFFECT_MSG_STARTED_LEARNING = 0;
- public static final int EFFECT_MSG_DONE_LEARNING = 1;
- public static final int EFFECT_MSG_SWITCHING_EFFECT = 2;
- public static final int EFFECT_MSG_EFFECTS_STOPPED = 3;
- public static final int EFFECT_MSG_RECORDING_DONE = 4;
- public static final int EFFECT_MSG_PREVIEW_RUNNING = 5;
-
- private Context mContext;
- private Handler mHandler;
-
- private CameraManager.CameraProxy mCameraDevice;
- private CamcorderProfile mProfile;
- private double mCaptureRate = 0;
- private SurfaceTexture mPreviewSurfaceTexture;
- private int mPreviewWidth;
- private int mPreviewHeight;
- private MediaRecorder.OnInfoListener mInfoListener;
- private MediaRecorder.OnErrorListener mErrorListener;
-
- private String mOutputFile;
- private FileDescriptor mFd;
- private int mOrientationHint = 0;
- private long mMaxFileSize = 0;
- private int mMaxDurationMs = 0;
- private int mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
- private int mCameraDisplayOrientation;
-
- private int mEffect = EFFECT_NONE;
- private int mCurrentEffect = EFFECT_NONE;
- private EffectsListener mEffectsListener;
-
- private Object mEffectParameter;
-
- private Object mGraphEnv;
- private int mGraphId;
- private Object mRunner = null;
- private Object mOldRunner = null;
-
- private SurfaceTexture mTextureSource;
-
- private static final int STATE_CONFIGURE = 0;
- private static final int STATE_WAITING_FOR_SURFACE = 1;
- private static final int STATE_STARTING_PREVIEW = 2;
- private static final int STATE_PREVIEW = 3;
- private static final int STATE_RECORD = 4;
- private static final int STATE_RELEASED = 5;
- private int mState = STATE_CONFIGURE;
-
- private boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
- private SoundClips.Player mSoundPlayer;
-
- /** Determine if a given effect is supported at runtime
- * Some effects require libraries not available on all devices
- */
- public static boolean isEffectSupported(int effectId) {
- if (sFilterIsAvailable == null) return false;
-
- try {
- switch (effectId) {
- case EFFECT_GOOFY_FACE:
- return (Boolean) sFilterIsAvailable.invoke(null,
- "com.google.android.filterpacks.facedetect.GoofyRenderFilter");
- case EFFECT_BACKDROPPER:
- return (Boolean) sFilterIsAvailable.invoke(null,
- "android.filterpacks.videoproc.BackDropperFilter");
- default:
- return false;
- }
- } catch (Exception ex) {
- Log.e(TAG, "Fail to check filter", ex);
- }
- return false;
- }
-
- public EffectsRecorder(Context context) {
- if (mLogVerbose) Log.v(TAG, "EffectsRecorder created (" + this + ")");
-
- if (!sReflectionInited) {
- try {
- sFilterSetInputValue = sClassFilter.getMethod("setInputValue",
- new Class[] {String.class, Object.class});
-
- Class<?> clsPoint = Class.forName("android.filterfw.geometry.Point");
- sCtPoint = clsPoint.getConstructor(new Class[] {float.class,
- float.class});
-
- Class<?> clsQuad = Class.forName("android.filterfw.geometry.Quad");
- sCtQuad = clsQuad.getConstructor(new Class[] {clsPoint, clsPoint,
- clsPoint, clsPoint});
-
- Class<?> clsBackDropperFilter = Class.forName(
- "android.filterpacks.videoproc.BackDropperFilter");
- sClsLearningDoneListener = Class.forName(
- "android.filterpacks.videoproc.BackDropperFilter$LearningDoneListener");
- sLearningDoneListenerOnLearningDone = sClsLearningDoneListener
- .getMethod("onLearningDone", new Class[] {clsBackDropperFilter});
-
- sObjectEquals = Object.class.getMethod("equals", new Class[] {Object.class});
- sObjectToString = Object.class.getMethod("toString");
-
- sClsOnRunnerDoneListener = Class.forName(
- "android.filterfw.core.GraphRunner$OnRunnerDoneListener");
- sOnRunnerDoneListenerOnRunnerDone = sClsOnRunnerDoneListener.getMethod(
- "onRunnerDone", new Class[] {int.class});
-
- sClsGraphRunner = Class.forName("android.filterfw.core.GraphRunner");
- sGraphRunnerGetGraph = sClsGraphRunner.getMethod("getGraph");
- sGraphRunnerSetDoneCallback = sClsGraphRunner.getMethod(
- "setDoneCallback", new Class[] {sClsOnRunnerDoneListener});
- sGraphRunnerRun = sClsGraphRunner.getMethod("run");
- sGraphRunnerGetError = sClsGraphRunner.getMethod("getError");
- sGraphRunnerStop = sClsGraphRunner.getMethod("stop");
-
- Class<?> clsFilterContext = Class.forName("android.filterfw.core.FilterContext");
- sFilterContextGetGLEnvironment = clsFilterContext.getMethod(
- "getGLEnvironment");
-
- Class<?> clsFilterGraph = Class.forName("android.filterfw.core.FilterGraph");
- sFilterGraphGetFilter = clsFilterGraph.getMethod("getFilter",
- new Class[] {String.class});
- sFilterGraphTearDown = clsFilterGraph.getMethod("tearDown",
- new Class[] {clsFilterContext});
-
- sClsGraphEnvironment = Class.forName("android.filterfw.GraphEnvironment");
- sCtGraphEnvironment = sClsGraphEnvironment.getConstructor();
- sGraphEnvironmentCreateGLEnvironment = sClsGraphEnvironment.getMethod(
- "createGLEnvironment");
- sGraphEnvironmentGetRunner = sClsGraphEnvironment.getMethod(
- "getRunner", new Class[] {int.class, int.class});
- sGraphEnvironmentAddReferences = sClsGraphEnvironment.getMethod(
- "addReferences", new Class[] {Object[].class});
- sGraphEnvironmentLoadGraph = sClsGraphEnvironment.getMethod(
- "loadGraph", new Class[] {Context.class, int.class});
- sGraphEnvironmentGetContext = sClsGraphEnvironment.getMethod(
- "getContext");
-
- Class<?> clsGLEnvironment = Class.forName("android.filterfw.core.GLEnvironment");
- sGLEnvironmentIsActive = clsGLEnvironment.getMethod("isActive");
- sGLEnvironmentActivate = clsGLEnvironment.getMethod("activate");
- sGLEnvironmentDeactivate = clsGLEnvironment.getMethod("deactivate");
-
- Class<?> clsSurfaceTextureTarget = Class.forName(
- "android.filterpacks.videosrc.SurfaceTextureTarget");
- sSurfaceTextureTargetDisconnect = clsSurfaceTextureTarget.getMethod(
- "disconnect", new Class[] {clsFilterContext});
-
- sClsOnRecordingDoneListener = Class.forName(
- "android.filterpacks.videosink.MediaEncoderFilter$OnRecordingDoneListener");
- sOnRecordingDoneListenerOnRecordingDone =
- sClsOnRecordingDoneListener.getMethod("onRecordingDone");
-
- sClsSurfaceTextureSourceListener = Class.forName(
- "android.filterpacks.videosrc.SurfaceTextureSource$SurfaceTextureSourceListener");
- sSurfaceTextureSourceListenerOnSurfaceTextureSourceReady =
- sClsSurfaceTextureSourceListener.getMethod(
- "onSurfaceTextureSourceReady",
- new Class[] {SurfaceTexture.class});
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
-
- sReflectionInited = true;
- }
-
- sEffectsRecorderIndex++;
- Log.v(TAG, "Current effects recorder index is " + sEffectsRecorderIndex);
- sEffectsRecorder = this;
- SerializableInvocationHandler sih = new SerializableInvocationHandler(
- sEffectsRecorderIndex);
- mLearningDoneListener = Proxy.newProxyInstance(
- sClsLearningDoneListener.getClassLoader(),
- new Class[] {sClsLearningDoneListener}, sih);
- mRunnerDoneCallback = Proxy.newProxyInstance(
- sClsOnRunnerDoneListener.getClassLoader(),
- new Class[] {sClsOnRunnerDoneListener}, sih);
- mSourceReadyCallback = Proxy.newProxyInstance(
- sClsSurfaceTextureSourceListener.getClassLoader(),
- new Class[] {sClsSurfaceTextureSourceListener}, sih);
- mRecordingDoneListener = Proxy.newProxyInstance(
- sClsOnRecordingDoneListener.getClassLoader(),
- new Class[] {sClsOnRecordingDoneListener}, sih);
-
- mContext = context;
- mHandler = new Handler(Looper.getMainLooper());
- mSoundPlayer = SoundClips.getPlayer(context);
- }
-
- public synchronized void setCamera(CameraManager.CameraProxy cameraDevice) {
- switch (mState) {
- case STATE_PREVIEW:
- throw new RuntimeException("setCamera cannot be called while previewing!");
- case STATE_RECORD:
- throw new RuntimeException("setCamera cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setCamera called on an already released recorder!");
- default:
- break;
- }
-
- mCameraDevice = cameraDevice;
- }
-
- public void setProfile(CamcorderProfile profile) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setProfile cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setProfile called on an already released recorder!");
- default:
- break;
- }
- mProfile = profile;
- }
-
- public void setOutputFile(String outputFile) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setOutputFile cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setOutputFile called on an already released recorder!");
- default:
- break;
- }
-
- mOutputFile = outputFile;
- mFd = null;
- }
-
- public void setOutputFile(FileDescriptor fd) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setOutputFile cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setOutputFile called on an already released recorder!");
- default:
- break;
- }
-
- mOutputFile = null;
- mFd = fd;
- }
-
- /**
- * Sets the maximum filesize (in bytes) of the recording session.
- * This will be passed on to the MediaEncoderFilter and then to the
- * MediaRecorder ultimately. If zero or negative, the MediaRecorder will
- * disable the limit
- */
- public synchronized void setMaxFileSize(long maxFileSize) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setMaxFileSize cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setMaxFileSize called on an already released recorder!");
- default:
- break;
- }
- mMaxFileSize = maxFileSize;
- }
-
- /**
- * Sets the maximum recording duration (in ms) for the next recording session
- * Setting it to zero (the default) disables the limit.
- */
- public synchronized void setMaxDuration(int maxDurationMs) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setMaxDuration cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setMaxDuration called on an already released recorder!");
- default:
- break;
- }
- mMaxDurationMs = maxDurationMs;
- }
-
-
- public void setCaptureRate(double fps) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setCaptureRate cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setCaptureRate called on an already released recorder!");
- default:
- break;
- }
-
- if (mLogVerbose) Log.v(TAG, "Setting time lapse capture rate to " + fps + " fps");
- mCaptureRate = fps;
- }
-
- public void setPreviewSurfaceTexture(SurfaceTexture previewSurfaceTexture,
- int previewWidth,
- int previewHeight) {
- if (mLogVerbose) Log.v(TAG, "setPreviewSurfaceTexture(" + this + ")");
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException(
- "setPreviewSurfaceTexture cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setPreviewSurfaceTexture called on an already released recorder!");
- default:
- break;
- }
-
- mPreviewSurfaceTexture = previewSurfaceTexture;
- mPreviewWidth = previewWidth;
- mPreviewHeight = previewHeight;
-
- switch (mState) {
- case STATE_WAITING_FOR_SURFACE:
- startPreview();
- break;
- case STATE_STARTING_PREVIEW:
- case STATE_PREVIEW:
- initializeEffect(true);
- break;
- }
- }
-
- public void setEffect(int effect, Object effectParameter) {
- if (mLogVerbose) Log.v(TAG,
- "setEffect: effect ID " + effect +
- ", parameter " + effectParameter.toString());
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setEffect cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setEffect called on an already released recorder!");
- default:
- break;
- }
-
- mEffect = effect;
- mEffectParameter = effectParameter;
-
- if (mState == STATE_PREVIEW ||
- mState == STATE_STARTING_PREVIEW) {
- initializeEffect(false);
- }
- }
-
- public interface EffectsListener {
- public void onEffectsUpdate(int effectId, int effectMsg);
- public void onEffectsError(Exception exception, String filePath);
- }
-
- public void setEffectsListener(EffectsListener listener) {
- mEffectsListener = listener;
- }
-
- private void setFaceDetectOrientation() {
- if (mCurrentEffect == EFFECT_GOOFY_FACE) {
- Object rotateFilter = getGraphFilter(mRunner, "rotate");
- Object metaRotateFilter = getGraphFilter(mRunner, "metarotate");
- setInputValue(rotateFilter, "rotation", mOrientationHint);
- int reverseDegrees = (360 - mOrientationHint) % 360;
- setInputValue(metaRotateFilter, "rotation", reverseDegrees);
- }
- }
-
- private void setRecordingOrientation() {
- if (mState != STATE_RECORD && mRunner != null) {
- Object bl = newInstance(sCtPoint, new Object[] {0, 0});
- Object br = newInstance(sCtPoint, new Object[] {1, 0});
- Object tl = newInstance(sCtPoint, new Object[] {0, 1});
- Object tr = newInstance(sCtPoint, new Object[] {1, 1});
- Object recordingRegion;
- if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
- // The back camera is not mirrored, so use a identity transform
- recordingRegion = newInstance(sCtQuad, new Object[] {bl, br, tl, tr});
- } else {
- // Recording region needs to be tweaked for front cameras, since they
- // mirror their preview
- if (mOrientationHint == 0 || mOrientationHint == 180) {
- // Horizontal flip in landscape
- recordingRegion = newInstance(sCtQuad, new Object[] {br, bl, tr, tl});
- } else {
- // Horizontal flip in portrait
- recordingRegion = newInstance(sCtQuad, new Object[] {tl, tr, bl, br});
- }
- }
- Object recorder = getGraphFilter(mRunner, "recorder");
- setInputValue(recorder, "inputRegion", recordingRegion);
- }
- }
- public void setOrientationHint(int degrees) {
- switch (mState) {
- case STATE_RELEASED:
- throw new RuntimeException(
- "setOrientationHint called on an already released recorder!");
- default:
- break;
- }
- if (mLogVerbose) Log.v(TAG, "Setting orientation hint to: " + degrees);
- mOrientationHint = degrees;
- setFaceDetectOrientation();
- setRecordingOrientation();
- }
-
- public void setCameraDisplayOrientation(int orientation) {
- if (mState != STATE_CONFIGURE) {
- throw new RuntimeException(
- "setCameraDisplayOrientation called after configuration!");
- }
- mCameraDisplayOrientation = orientation;
- }
-
- public void setCameraFacing(int facing) {
- switch (mState) {
- case STATE_RELEASED:
- throw new RuntimeException(
- "setCameraFacing called on alrady released recorder!");
- default:
- break;
- }
- mCameraFacing = facing;
- setRecordingOrientation();
- }
-
- public void setOnInfoListener(MediaRecorder.OnInfoListener infoListener) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setInfoListener cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setInfoListener called on an already released recorder!");
- default:
- break;
- }
- mInfoListener = infoListener;
- }
-
- public void setOnErrorListener(MediaRecorder.OnErrorListener errorListener) {
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("setErrorListener cannot be called while recording!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "setErrorListener called on an already released recorder!");
- default:
- break;
- }
- mErrorListener = errorListener;
- }
-
- private void initializeFilterFramework() {
- mGraphEnv = newInstance(sCtGraphEnvironment);
- invoke(mGraphEnv, sGraphEnvironmentCreateGLEnvironment);
-
- int videoFrameWidth = mProfile.videoFrameWidth;
- int videoFrameHeight = mProfile.videoFrameHeight;
- if (mCameraDisplayOrientation == 90 || mCameraDisplayOrientation == 270) {
- int tmp = videoFrameWidth;
- videoFrameWidth = videoFrameHeight;
- videoFrameHeight = tmp;
- }
-
- invoke(mGraphEnv, sGraphEnvironmentAddReferences,
- new Object[] {new Object[] {
- "textureSourceCallback", mSourceReadyCallback,
- "recordingWidth", videoFrameWidth,
- "recordingHeight", videoFrameHeight,
- "recordingProfile", mProfile,
- "learningDoneListener", mLearningDoneListener,
- "recordingDoneListener", mRecordingDoneListener}});
- mRunner = null;
- mGraphId = -1;
- mCurrentEffect = EFFECT_NONE;
- }
-
- private synchronized void initializeEffect(boolean forceReset) {
- if (forceReset ||
- mCurrentEffect != mEffect ||
- mCurrentEffect == EFFECT_BACKDROPPER) {
-
- invoke(mGraphEnv, sGraphEnvironmentAddReferences,
- new Object[] {new Object[] {
- "previewSurfaceTexture", mPreviewSurfaceTexture,
- "previewWidth", mPreviewWidth,
- "previewHeight", mPreviewHeight,
- "orientation", mOrientationHint}});
- if (mState == STATE_PREVIEW ||
- mState == STATE_STARTING_PREVIEW) {
- // Switching effects while running. Inform video camera.
- sendMessage(mCurrentEffect, EFFECT_MSG_SWITCHING_EFFECT);
- }
-
- switch (mEffect) {
- case EFFECT_GOOFY_FACE:
- mGraphId = (Integer) invoke(mGraphEnv,
- sGraphEnvironmentLoadGraph,
- new Object[] {mContext, R.raw.goofy_face});
- break;
- case EFFECT_BACKDROPPER:
- sendMessage(EFFECT_BACKDROPPER, EFFECT_MSG_STARTED_LEARNING);
- mGraphId = (Integer) invoke(mGraphEnv,
- sGraphEnvironmentLoadGraph,
- new Object[] {mContext, R.raw.backdropper});
- break;
- default:
- throw new RuntimeException("Unknown effect ID" + mEffect + "!");
- }
- mCurrentEffect = mEffect;
-
- mOldRunner = mRunner;
- mRunner = invoke(mGraphEnv, sGraphEnvironmentGetRunner,
- new Object[] {mGraphId,
- getConstant(sClsGraphEnvironment, "MODE_ASYNCHRONOUS")});
- invoke(mRunner, sGraphRunnerSetDoneCallback, new Object[] {mRunnerDoneCallback});
- if (mLogVerbose) {
- Log.v(TAG, "New runner: " + mRunner
- + ". Old runner: " + mOldRunner);
- }
- if (mState == STATE_PREVIEW ||
- mState == STATE_STARTING_PREVIEW) {
- // Switching effects while running. Stop existing runner.
- // The stop callback will take care of starting new runner.
- mCameraDevice.stopPreview();
- mCameraDevice.setPreviewTexture(null);
- invoke(mOldRunner, sGraphRunnerStop);
- }
- }
-
- switch (mCurrentEffect) {
- case EFFECT_GOOFY_FACE:
- tryEnableVideoStabilization(true);
- Object goofyFilter = getGraphFilter(mRunner, "goofyrenderer");
- setInputValue(goofyFilter, "currentEffect",
- ((Integer) mEffectParameter).intValue());
- break;
- case EFFECT_BACKDROPPER:
- tryEnableVideoStabilization(false);
- Object backgroundSrc = getGraphFilter(mRunner, "background");
- if (ApiHelper.HAS_EFFECTS_RECORDING_CONTEXT_INPUT) {
- // Set the context first before setting sourceUrl to
- // guarantee the content URI get resolved properly.
- setInputValue(backgroundSrc, "context", mContext);
- }
- setInputValue(backgroundSrc, "sourceUrl", mEffectParameter);
- // For front camera, the background video needs to be mirrored in the
- // backdropper filter
- if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- Object replacer = getGraphFilter(mRunner, "replacer");
- setInputValue(replacer, "mirrorBg", true);
- if (mLogVerbose) Log.v(TAG, "Setting the background to be mirrored");
- }
- break;
- default:
- break;
- }
- setFaceDetectOrientation();
- setRecordingOrientation();
- }
-
- public synchronized void startPreview() {
- if (mLogVerbose) Log.v(TAG, "Starting preview (" + this + ")");
-
- switch (mState) {
- case STATE_STARTING_PREVIEW:
- case STATE_PREVIEW:
- // Already running preview
- Log.w(TAG, "startPreview called when already running preview");
- return;
- case STATE_RECORD:
- throw new RuntimeException("Cannot start preview when already recording!");
- case STATE_RELEASED:
- throw new RuntimeException("setEffect called on an already released recorder!");
- default:
- break;
- }
-
- if (mEffect == EFFECT_NONE) {
- throw new RuntimeException("No effect selected!");
- }
- if (mEffectParameter == null) {
- throw new RuntimeException("No effect parameter provided!");
- }
- if (mProfile == null) {
- throw new RuntimeException("No recording profile provided!");
- }
- if (mPreviewSurfaceTexture == null) {
- if (mLogVerbose) Log.v(TAG, "Passed a null surface; waiting for valid one");
- mState = STATE_WAITING_FOR_SURFACE;
- return;
- }
- if (mCameraDevice == null) {
- throw new RuntimeException("No camera to record from!");
- }
-
- if (mLogVerbose) Log.v(TAG, "Initializing filter framework and running the graph.");
- initializeFilterFramework();
-
- initializeEffect(true);
-
- mState = STATE_STARTING_PREVIEW;
- invoke(mRunner, sGraphRunnerRun);
- // Rest of preview startup handled in mSourceReadyCallback
- }
-
- private Object invokeObjectEquals(Object proxy, Object[] args) {
- return Boolean.valueOf(proxy == args[0]);
- }
-
- private Object invokeObjectToString() {
- return "Proxy-" + toString();
- }
-
- private void invokeOnLearningDone() {
- if (mLogVerbose) Log.v(TAG, "Learning done callback triggered");
- // Called in a processing thread, so have to post message back to UI
- // thread
- sendMessage(EFFECT_BACKDROPPER, EFFECT_MSG_DONE_LEARNING);
- enable3ALocks(true);
- }
-
- private void invokeOnRunnerDone(Object[] args) {
- int runnerDoneResult = (Integer) args[0];
- synchronized (EffectsRecorder.this) {
- if (mLogVerbose) {
- Log.v(TAG,
- "Graph runner done (" + EffectsRecorder.this
- + ", mRunner " + mRunner
- + ", mOldRunner " + mOldRunner + ")");
- }
- if (runnerDoneResult ==
- (Integer) getConstant(sClsGraphRunner, "RESULT_ERROR")) {
- // Handle error case
- Log.e(TAG, "Error running filter graph!");
- Exception e = null;
- if (mRunner != null) {
- e = (Exception) invoke(mRunner, sGraphRunnerGetError);
- } else if (mOldRunner != null) {
- e = (Exception) invoke(mOldRunner, sGraphRunnerGetError);
- }
- raiseError(e);
- }
- if (mOldRunner != null) {
- // Tear down old graph if available
- if (mLogVerbose) Log.v(TAG, "Tearing down old graph.");
- Object glEnv = getContextGLEnvironment(mGraphEnv);
- if (glEnv != null && !(Boolean) invoke(glEnv, sGLEnvironmentIsActive)) {
- invoke(glEnv, sGLEnvironmentActivate);
- }
- getGraphTearDown(mOldRunner,
- invoke(mGraphEnv, sGraphEnvironmentGetContext));
- if (glEnv != null && (Boolean) invoke(glEnv, sGLEnvironmentIsActive)) {
- invoke(glEnv, sGLEnvironmentDeactivate);
- }
- mOldRunner = null;
- }
- if (mState == STATE_PREVIEW ||
- mState == STATE_STARTING_PREVIEW) {
- // Switching effects, start up the new runner
- if (mLogVerbose) {
- Log.v(TAG, "Previous effect halted. Running graph again. state: "
- + mState);
- }
- tryEnable3ALocks(false);
- // In case of an error, the graph restarts from beginning and in case
- // of the BACKDROPPER effect, the learner re-learns the background.
- // Hence, we need to show the learning dialogue to the user
- // to avoid recording before the learning is done. Else, the user
- // could start recording before the learning is done and the new
- // background comes up later leading to an end result video
- // with a heterogeneous background.
- // For BACKDROPPER effect, this path is also executed sometimes at
- // the end of a normal recording session. In such a case, the graph
- // does not restart and hence the learner does not re-learn. So we
- // do not want to show the learning dialogue then.
- if (runnerDoneResult == (Integer) getConstant(
- sClsGraphRunner, "RESULT_ERROR")
- && mCurrentEffect == EFFECT_BACKDROPPER) {
- sendMessage(EFFECT_BACKDROPPER, EFFECT_MSG_STARTED_LEARNING);
- }
- invoke(mRunner, sGraphRunnerRun);
- } else if (mState != STATE_RELEASED) {
- // Shutting down effects
- if (mLogVerbose) Log.v(TAG, "Runner halted, restoring direct preview");
- tryEnable3ALocks(false);
- sendMessage(EFFECT_NONE, EFFECT_MSG_EFFECTS_STOPPED);
- } else {
- // STATE_RELEASED - camera will be/has been released as well, do nothing.
- }
- }
- }
-
- private void invokeOnSurfaceTextureSourceReady(Object[] args) {
- SurfaceTexture source = (SurfaceTexture) args[0];
- if (mLogVerbose) Log.v(TAG, "SurfaceTexture ready callback received");
- synchronized (EffectsRecorder.this) {
- mTextureSource = source;
-
- if (mState == STATE_CONFIGURE) {
- // Stop preview happened while the runner was doing startup tasks
- // Since we haven't started anything up, don't do anything
- // Rest of cleanup will happen in onRunnerDone
- if (mLogVerbose) Log.v(TAG, "Ready callback: Already stopped, skipping.");
- return;
- }
- if (mState == STATE_RELEASED) {
- // EffectsRecorder has been released, so don't touch the camera device
- // or anything else
- if (mLogVerbose) Log.v(TAG, "Ready callback: Already released, skipping.");
- return;
- }
- if (source == null) {
- if (mLogVerbose) {
- Log.v(TAG, "Ready callback: source null! Looks like graph was closed!");
- }
- if (mState == STATE_PREVIEW ||
- mState == STATE_STARTING_PREVIEW ||
- mState == STATE_RECORD) {
- // A null source here means the graph is shutting down
- // unexpectedly, so we need to turn off preview before
- // the surface texture goes away.
- if (mLogVerbose) {
- Log.v(TAG, "Ready callback: State: " + mState
- + ". stopCameraPreview");
- }
-
- stopCameraPreview();
- }
- return;
- }
-
- // Lock AE/AWB to reduce transition flicker
- tryEnable3ALocks(true);
-
- mCameraDevice.stopPreview();
- if (mLogVerbose) Log.v(TAG, "Runner active, connecting effects preview");
- mCameraDevice.setPreviewTexture(mTextureSource);
-
- mCameraDevice.startPreview();
-
- // Unlock AE/AWB after preview started
- tryEnable3ALocks(false);
-
- mState = STATE_PREVIEW;
-
- if (mLogVerbose) Log.v(TAG, "Start preview/effect switch complete");
-
- // Sending a message to listener that preview is complete
- sendMessage(mCurrentEffect, EFFECT_MSG_PREVIEW_RUNNING);
- }
- }
-
- private void invokeOnRecordingDone() {
- // Forward the callback to the VideoModule object (as an asynchronous event).
- if (mLogVerbose) Log.v(TAG, "Recording done callback triggered");
- sendMessage(EFFECT_NONE, EFFECT_MSG_RECORDING_DONE);
- }
-
- public synchronized void startRecording() {
- if (mLogVerbose) Log.v(TAG, "Starting recording (" + this + ")");
-
- switch (mState) {
- case STATE_RECORD:
- throw new RuntimeException("Already recording, cannot begin anew!");
- case STATE_RELEASED:
- throw new RuntimeException(
- "startRecording called on an already released recorder!");
- default:
- break;
- }
-
- if ((mOutputFile == null) && (mFd == null)) {
- throw new RuntimeException("No output file name or descriptor provided!");
- }
-
- if (mState == STATE_CONFIGURE) {
- startPreview();
- }
-
- Object recorder = getGraphFilter(mRunner, "recorder");
- if (mFd != null) {
- setInputValue(recorder, "outputFileDescriptor", mFd);
- } else {
- setInputValue(recorder, "outputFile", mOutputFile);
- }
- // It is ok to set the audiosource without checking for timelapse here
- // since that check will be done in the MediaEncoderFilter itself
- setInputValue(recorder, "audioSource", MediaRecorder.AudioSource.CAMCORDER);
- setInputValue(recorder, "recordingProfile", mProfile);
- setInputValue(recorder, "orientationHint", mOrientationHint);
- // Important to set the timelapseinterval to 0 if the capture rate is not >0
- // since the recorder does not get created every time the recording starts.
- // The recorder infers whether the capture is timelapsed based on the value of
- // this interval
- boolean captureTimeLapse = mCaptureRate > 0;
- if (captureTimeLapse) {
- double timeBetweenFrameCapture = 1 / mCaptureRate;
- setInputValue(recorder, "timelapseRecordingIntervalUs",
- (long) (1000000 * timeBetweenFrameCapture));
-
- } else {
- setInputValue(recorder, "timelapseRecordingIntervalUs", 0L);
- }
-
- if (mInfoListener != null) {
- setInputValue(recorder, "infoListener", mInfoListener);
- }
- if (mErrorListener != null) {
- setInputValue(recorder, "errorListener", mErrorListener);
- }
- setInputValue(recorder, "maxFileSize", mMaxFileSize);
- setInputValue(recorder, "maxDurationMs", mMaxDurationMs);
- setInputValue(recorder, "recording", true);
- mSoundPlayer.play(SoundClips.START_VIDEO_RECORDING);
- mState = STATE_RECORD;
- }
-
- public synchronized void stopRecording() {
- if (mLogVerbose) Log.v(TAG, "Stop recording (" + this + ")");
-
- switch (mState) {
- case STATE_CONFIGURE:
- case STATE_STARTING_PREVIEW:
- case STATE_PREVIEW:
- Log.w(TAG, "StopRecording called when recording not active!");
- return;
- case STATE_RELEASED:
- throw new RuntimeException("stopRecording called on released EffectsRecorder!");
- default:
- break;
- }
- Object recorder = getGraphFilter(mRunner, "recorder");
- setInputValue(recorder, "recording", false);
- mSoundPlayer.play(SoundClips.STOP_VIDEO_RECORDING);
- mState = STATE_PREVIEW;
- }
-
- // Called to tell the filter graph that the display surfacetexture is not valid anymore.
- // So the filter graph should not hold any reference to the surface created with that.
- public synchronized void disconnectDisplay() {
- if (mLogVerbose) Log.v(TAG, "Disconnecting the graph from the " +
- "SurfaceTexture");
- Object display = getGraphFilter(mRunner, "display");
- invoke(display, sSurfaceTextureTargetDisconnect, new Object[] {
- invoke(mGraphEnv, sGraphEnvironmentGetContext)});
- }
-
- // The VideoModule will call this to notify that the camera is being
- // released to the outside world. This call should happen after the
- // stopRecording call. Else, the effects may throw an exception.
- // With the recording stopped, the stopPreview call will not try to
- // release the camera again.
- // This must be called in onPause() if the effects are ON.
- public synchronized void disconnectCamera() {
- if (mLogVerbose) Log.v(TAG, "Disconnecting the effects from Camera");
- stopCameraPreview();
- mCameraDevice = null;
- }
-
- // In a normal case, when the disconnect is not called, we should not
- // set the camera device to null, since on return callback, we try to
- // enable 3A locks, which need the cameradevice.
- public synchronized void stopCameraPreview() {
- if (mLogVerbose) Log.v(TAG, "Stopping camera preview.");
- if (mCameraDevice == null) {
- Log.d(TAG, "Camera already null. Nothing to disconnect");
- return;
- }
- mCameraDevice.stopPreview();
- mCameraDevice.setPreviewTexture(null);
- }
-
- // Stop and release effect resources
- public synchronized void stopPreview() {
- if (mLogVerbose) Log.v(TAG, "Stopping preview (" + this + ")");
- switch (mState) {
- case STATE_CONFIGURE:
- Log.w(TAG, "StopPreview called when preview not active!");
- return;
- case STATE_RELEASED:
- throw new RuntimeException("stopPreview called on released EffectsRecorder!");
- default:
- break;
- }
-
- if (mState == STATE_RECORD) {
- stopRecording();
- }
-
- mCurrentEffect = EFFECT_NONE;
-
- // This will not do anything if the camera has already been disconnected.
- stopCameraPreview();
-
- mState = STATE_CONFIGURE;
- mOldRunner = mRunner;
- invoke(mRunner, sGraphRunnerStop);
- mRunner = null;
- // Rest of stop and release handled in mRunnerDoneCallback
- }
-
- // Try to enable/disable video stabilization if supported; otherwise return false
- // It is called from a synchronized block.
- boolean tryEnableVideoStabilization(boolean toggle) {
- if (mLogVerbose) Log.v(TAG, "tryEnableVideoStabilization.");
- if (mCameraDevice == null) {
- Log.d(TAG, "Camera already null. Not enabling video stabilization.");
- return false;
- }
- Camera.Parameters params = mCameraDevice.getParameters();
-
- String vstabSupported = params.get("video-stabilization-supported");
- if ("true".equals(vstabSupported)) {
- if (mLogVerbose) Log.v(TAG, "Setting video stabilization to " + toggle);
- params.set("video-stabilization", toggle ? "true" : "false");
- mCameraDevice.setParameters(params);
- return true;
- }
- if (mLogVerbose) Log.v(TAG, "Video stabilization not supported");
- return false;
- }
-
- // Try to enable/disable 3A locks if supported; otherwise return false
- @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
- synchronized boolean tryEnable3ALocks(boolean toggle) {
- if (mLogVerbose) Log.v(TAG, "tryEnable3ALocks");
- if (mCameraDevice == null) {
- Log.d(TAG, "Camera already null. Not tryenabling 3A locks.");
- return false;
- }
- Camera.Parameters params = mCameraDevice.getParameters();
- if (CameraUtil.isAutoExposureLockSupported(params) &&
- CameraUtil.isAutoWhiteBalanceLockSupported(params)) {
- params.setAutoExposureLock(toggle);
- params.setAutoWhiteBalanceLock(toggle);
- mCameraDevice.setParameters(params);
- return true;
- }
- return false;
- }
-
- // Try to enable/disable 3A locks if supported; otherwise, throw error
- // Use this when locks are essential to success
- synchronized void enable3ALocks(boolean toggle) {
- if (mLogVerbose) Log.v(TAG, "Enable3ALocks");
- if (mCameraDevice == null) {
- Log.d(TAG, "Camera already null. Not enabling 3A locks.");
- return;
- }
- Camera.Parameters params = mCameraDevice.getParameters();
- if (!tryEnable3ALocks(toggle)) {
- throw new RuntimeException("Attempt to lock 3A on camera with no locking support!");
- }
- }
-
- static class SerializableInvocationHandler
- implements InvocationHandler, Serializable {
- private final int mEffectsRecorderIndex;
- public SerializableInvocationHandler(int index) {
- mEffectsRecorderIndex = index;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- if (sEffectsRecorder == null) return null;
- if (mEffectsRecorderIndex != sEffectsRecorderIndex) {
- Log.v(TAG, "Ignore old callback " + mEffectsRecorderIndex);
- return null;
- }
- if (method.equals(sObjectEquals)) {
- return sEffectsRecorder.invokeObjectEquals(proxy, args);
- } else if (method.equals(sObjectToString)) {
- return sEffectsRecorder.invokeObjectToString();
- } else if (method.equals(sLearningDoneListenerOnLearningDone)) {
- sEffectsRecorder.invokeOnLearningDone();
- } else if (method.equals(sOnRunnerDoneListenerOnRunnerDone)) {
- sEffectsRecorder.invokeOnRunnerDone(args);
- } else if (method.equals(
- sSurfaceTextureSourceListenerOnSurfaceTextureSourceReady)) {
- sEffectsRecorder.invokeOnSurfaceTextureSourceReady(args);
- } else if (method.equals(sOnRecordingDoneListenerOnRecordingDone)) {
- sEffectsRecorder.invokeOnRecordingDone();
- }
- return null;
- }
- }
-
- // Indicates that all camera/recording activity needs to halt
- public synchronized void release() {
- if (mLogVerbose) Log.v(TAG, "Releasing (" + this + ")");
-
- switch (mState) {
- case STATE_RECORD:
- case STATE_STARTING_PREVIEW:
- case STATE_PREVIEW:
- stopPreview();
- // Fall-through
- default:
- if (mSoundPlayer != null) {
- mSoundPlayer.release();
- mSoundPlayer = null;
- }
- mState = STATE_RELEASED;
- break;
- }
- sEffectsRecorder = null;
- }
-
- private void sendMessage(final int effect, final int msg) {
- if (mEffectsListener != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mEffectsListener.onEffectsUpdate(effect, msg);
- }
- });
- }
- }
-
- private void raiseError(final Exception exception) {
- if (mEffectsListener != null) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mFd != null) {
- mEffectsListener.onEffectsError(exception, null);
- } else {
- mEffectsListener.onEffectsError(exception, mOutputFile);
- }
- }
- });
- }
- }
-
- // invoke method on receiver with no arguments
- private Object invoke(Object receiver, Method method) {
- try {
- return method.invoke(receiver);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- // invoke method on receiver with arguments
- private Object invoke(Object receiver, Method method, Object[] args) {
- try {
- return method.invoke(receiver, args);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private void setInputValue(Object receiver, String key, Object value) {
- try {
- sFilterSetInputValue.invoke(receiver, new Object[] {key, value});
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private Object newInstance(Constructor<?> ct, Object[] initArgs) {
- try {
- return ct.newInstance(initArgs);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private Object newInstance(Constructor<?> ct) {
- try {
- return ct.newInstance();
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private Object getGraphFilter(Object receiver, String name) {
- try {
- return sFilterGraphGetFilter.invoke(sGraphRunnerGetGraph
- .invoke(receiver), new Object[] {name});
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private Object getContextGLEnvironment(Object receiver) {
- try {
- return sFilterContextGetGLEnvironment
- .invoke(sGraphEnvironmentGetContext.invoke(receiver));
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private void getGraphTearDown(Object receiver, Object filterContext) {
- try {
- sFilterGraphTearDown.invoke(sGraphRunnerGetGraph.invoke(receiver),
- new Object[]{filterContext});
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- private Object getConstant(Class<?> cls, String name) {
- try {
- return cls.getDeclaredField(name).get(null);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-}