@Override
  public void process(FilterContext context) {
    // Get input frame
    Frame input = pullInput("image");
    FrameFormat inputFormat = input.getFormat();

    // Create program if not created already
    if (mProgram == null || inputFormat.getTarget() != mTarget) {
      initProgram(context, inputFormat.getTarget());
    }

    // Check if the frame size has changed
    if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
      mWidth = inputFormat.getWidth();
      mHeight = inputFormat.getHeight();
      initParameters();
    }

    // Create output frame
    Frame output = context.getFrameManager().newFrame(inputFormat);

    // Process
    mProgram.process(input, output);

    // Push output
    pushOutput("image", output);

    // Release pushed frame
    output.release();
  }
  @Override
  public void process(FilterContext env) {
    Frame input = pullInput("mixedcase");
    String inputString = (String) input.getObjectValue();

    Frame output = env.getFrameManager().newFrame(mOutputFormat);
    output.setObjectValue(inputString.toUpperCase(Locale.getDefault()));

    pushOutput("uppercase", output);
  }
  @Override
  protected void prepare(FilterContext context) {
    if (mLogVerbose) Log.v(TAG, "Preparing SurfaceTextureSource");

    createFormats();

    // Prepare input
    mMediaFrame =
        (GLFrame)
            context.getFrameManager().newBoundFrame(mOutputFormat, GLFrame.EXTERNAL_TEXTURE, 0);

    // Prepare output
    mFrameExtractor = new ShaderProgram(context, mRenderShader);
  }
  @Override
  public void process(FilterContext context) {
    if (mLogVerbose) Log.v(TAG, "Processing new frame");

    // First, get new frame if available
    if (mWaitForNewFrame || mFirstFrame) {
      boolean gotNewFrame;
      if (mWaitTimeout != 0) {
        gotNewFrame = mNewFrameAvailable.block(mWaitTimeout);
        if (!gotNewFrame) {
          if (!mCloseOnTimeout) {
            throw new RuntimeException("Timeout waiting for new frame");
          } else {
            if (mLogVerbose) Log.v(TAG, "Timeout waiting for a new frame. Closing.");
            closeOutputPort("video");
            return;
          }
        }
      } else {
        mNewFrameAvailable.block();
      }
      mNewFrameAvailable.close();
      mFirstFrame = false;
    }

    mSurfaceTexture.updateTexImage();

    mSurfaceTexture.getTransformMatrix(mFrameTransform);
    Matrix.multiplyMM(
        mMappedCoords, 0,
        mFrameTransform, 0,
        mSourceCoords, 0);
    mFrameExtractor.setSourceRegion(
        mMappedCoords[0], mMappedCoords[1],
        mMappedCoords[4], mMappedCoords[5],
        mMappedCoords[8], mMappedCoords[9],
        mMappedCoords[12], mMappedCoords[13]);
    // Next, render to output
    Frame output = context.getFrameManager().newFrame(mOutputFormat);
    mFrameExtractor.process(mMediaFrame, output);

    output.setTimestamp(mSurfaceTexture.getTimestamp());

    pushOutput("video", output);
    output.release();
  }
  @Override
  public void process(FilterContext context) {
    // Get input frame
    Frame input = pullInput("image");
    FrameFormat inputFormat = input.getFormat();

    // Create program if not created already
    if (mProgram == null || inputFormat.getTarget() != mTarget) {
      initProgram(context, inputFormat.getTarget());
      updateParameters();
    }

    // Create output frame
    Frame output = context.getFrameManager().newFrame(inputFormat);

    // Process
    mProgram.process(input, output);

    // Push output
    pushOutput("image", output);

    // Release pushed frame
    output.release();
  }
  @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;
  }