@Override
  public void open(FilterContext context, VideoEventFilter myfilter) {
    super.open(context, myfilter);
    mCopyProgram = new ShaderProgram(context, mOverlayShader);
    mCopyProgram.setHostValue("center_r", 0.0f);

    mIsGotPreviousFrame = false;
  }
  @Override
  public void close(FilterContext context, VideoEventFilter myfilter) {
    super.close(context, myfilter);

    if (null != mPreviousFrame) {
      mPreviousFrame.release();
      mPreviousFrame = null;
    }
  }
  @Override
  public boolean process(
      FilterContext context, VideoEventFilter myfilter, boolean isRenderOutput, GLFrame output) {
    super.process(context, myfilter, isRenderOutput, output);

    GLFrame camera = myfilter.getInputCameraGLFrame();

    FrameFormat inputFormat;
    inputFormat = camera.getFormat();

    if (null == mMainFrame) {
      MutableFrameFormat outputFormat = inputFormat.mutableCopy();
      mMainFrame = (GLFrame) context.getFrameManager().newFrame(outputFormat);
      mMainFrame.focus();
      mCopyProgramWithColor.process(camera, mMainFrame);
    }

    if (null == mPreviousFrame) {
      MutableFrameFormat outputFormat = inputFormat.mutableCopy();
      mPreviousFrame = (GLFrame) context.getFrameManager().newFrame(outputFormat);
      mPreviousFrame.focus();
    }

    Frame[] subtractInputs = {camera, mPreviousFrame};

    long currentTimeStamp = myfilter.getNowTimeStamp();
    long cameraPhoto;

    if (this.containsKey("camera_photo")) {
      try {
        cameraPhoto = ((Long) this.get("camera_photo")).longValue() + mStart;
      } catch (ClassCastException e) {
        e.printStackTrace();
        return false;
      }

      // get fix frame
      if (currentTimeStamp >= cameraPhoto) {
        if (false == mGotMainFrame) {
          mMainFrame.focus();
          mColor[3] = 1.0f;
          mCopyProgramWithColor.setHostValue("ccc", mColor);
          mCopyProgramWithColor.process(camera, mMainFrame);
          mGotMainFrame = true;
          mTool.log('d', "Got CameraInput:" + currentTimeStamp);
        }
        subtractInputs[0] = mMainFrame;
      }

      if (false == mIsGotPreviousFrame
          || true == mIsGotPreviousFrame && currentTimeStamp < mEffectStart) {
        mPreviousFrame.focus();
        if (isRenderOutput) {
          mCopyProgramWithColor.process(output, mPreviousFrame);
        } else {
          mCopyProgramWithColor.process(camera, mPreviousFrame);
        }
        mIsGotPreviousFrame = true;
        mTool.log('d', "Got PreviousInput:" + currentTimeStamp);
      }

      // finished, no need to do
      if (currentTimeStamp >= mEffectEnd || currentTimeStamp < mEffectStart) {
        return false;
      }

      float center_r = 0.0f;

      if (cameraPhoto >= currentTimeStamp) {
        center_r =
            1.0f - (float) (currentTimeStamp - mEffectStart) / (float) (cameraPhoto - mEffectStart);
        subtractInputs[0] = mPreviousFrame;
      } else {
        center_r = (float) (currentTimeStamp - cameraPhoto) / (float) (mEffectEnd - cameraPhoto);
        subtractInputs[0] = mMainFrame;
      }

      mCopyProgram.setHostValue("center_r", center_r);

      if (null != output && isRenderOutput == false) {
        mCopyProgram.process(subtractInputs, output);
      }
      return true;
    }
    return false;
  }