title: Screen Recorder Android Plugin in Unity
body: <p>I'm developing an Unity-Android Plugin to record game screen and create a mp4 video file.I follow to Android Breakout game recorder patch sample in this site : <a href="http://bigflake.com/mediacodec/" rel="noreferrer">http://bigflake.com/mediacodec/</a>.<br/>
  First, I create my CustomUnityPlayer class that extends UnityPlayer class and override onDrawFrame method.Here is my CustomUnityPlayer class code :</p>
  
  <pre><code>package com.example.screenrecorder;
  import javax.microedition.khronos.egl.EGLConfig;
  import javax.microedition.khronos.opengles.GL10;
  import android.content.ContextWrapper;
  import android.opengl.EGL14;
  import android.opengl.EGLContext;
  import android.opengl.EGLDisplay;
  import android.opengl.EGLSurface;
  import android.opengl.GLES20;
  import android.opengl.GLSurfaceView;
  import android.opengl.Matrix;
  import android.util.Log;
  
  
  import com.unity3d.player.*;
  
  public class CustomUnityPlayer extends UnityPlayer implements GLSurfaceView.Renderer {
  
  public static final String TAG = "ScreenRecord";
  public static final boolean EXTRA_CHECK = true;         // enable additional assertions
  private GameRecorder recorder;
  static final float mProjectionMatrix[] = new float[16];
  private final float mSavedMatrix[] = new float[16];
  private EGLDisplay mSavedEglDisplay;
  private EGLSurface mSavedEglDrawSurface;
  private EGLSurface mSavedEglReadSurface;
  private EGLContext mSavedEglContext;
  
  // Frame counter, used for reducing recorder frame rate.
  private int mFrameCount;
  
  static final float ARENA_WIDTH = 768.0f;
  static final float ARENA_HEIGHT = 1024.0f;
  
  private int mViewportWidth, mViewportHeight;
  private int mViewportXoff, mViewportYoff;
  
  
  
  private final float[] mViewMatrix = new float[16];
  private final float[] mRotationMatrix = new float[16];
  private float mAngle;
  
  public CustomUnityPlayer(ContextWrapper context) {
      // TODO Auto-generated constructor stub
      super(context);
      this.recorder = GameRecorder.getInstance();
  }
  
   private boolean recordThisFrame() {
          final int TARGET_FPS = 30;
  
          mFrameCount ++;
          switch (TARGET_FPS) {
          case 60:
              return true;
          case 30:
              return (mFrameCount &amp; 0x01) == 0;
          case 24:
              // want 2 out of every 5 frames
              int mod = mFrameCount % 5;
              return mod == 0 || mod == 2;
          default:
              return true;
          }
      }
  
  public void onDrawFrame(GL10 gl){
  
      //record this frame
      if (this.recorder.isRecording() &amp;&amp; this.recordThisFrame()) {    
  
          saveRenderState();
  
          // switch to recorder state
          this.recorder.makeCurrent();
          super.onDrawFrame(gl);
          this.recorder.getProjectionMatrix(mProjectionMatrix);
          this.recorder.setViewport();
  
          this.recorder.swapBuffers();    
  
          restoreRenderState();
      }
  }
  
  public void onSurfaceCreated(GL10 paramGL10, EGLConfig paramEGLConfig){
      // now repeat it for the game recorder
      if (this.recorder.isRecording()) {
          Log.d(TAG, "configuring GL for recorder");
          saveRenderState();
          this.recorder.firstTimeSetup();
          super.onSurfaceCreated(paramGL10, paramEGLConfig);
          this.recorder.makeCurrent();
          //glSetup();
          restoreRenderState();
  
          mFrameCount = 0;
      }
  
      if (EXTRA_CHECK) Util.checkGlError("onSurfaceCreated end");
  }
  
  public void onSurfaceChanged(GL10 unused, int width, int height) {
      /*
       * We want the viewport to be proportional to the arena size.  That way a 10x10
       * object in arena coordinates will look square on the screen, and our round ball
       * will look round.
       *
       * If we wanted to fill the entire screen with our game, we would want to adjust the
       * size of the arena itself, not just stretch it to fit the boundaries.  This can have
       * subtle effects on gameplay, e.g. the time it takes the ball to travel from the top
       * to the bottom of the screen will be different on a device with a 16:9 display than on
       * a 4:3 display.  Other games might address this differently, e.g. a side-scroller
       * could display a bit more of the level on the left and right.
       *
       * We do want to fill as much space as we can, so we should either be pressed up against
       * the left/right edges or top/bottom.
       *
       * Our game plays best in portrait mode.  We could force the app to run in portrait
       * mode (by setting a value in AndroidManifest, or by setting the projection to rotate
       * the world to match the longest screen dimension), but that's annoying, especially
       * on devices that don't rotate easily (e.g. plasma TVs).
       */
  
      super.onSurfaceChanged(unused, width, height);
      if (EXTRA_CHECK) Util.checkGlError("onSurfaceChanged start");
  
      float arenaRatio = ARENA_HEIGHT / ARENA_WIDTH;
      int x, y, viewWidth, viewHeight;
  
      if (height &gt; (int) (width * arenaRatio)) {
          // limited by narrow width; restrict height
          viewWidth = width;
          viewHeight = (int) (width * arenaRatio);
      } else {
          // limited by short height; restrict width
          viewHeight = height;
          viewWidth = (int) (height / arenaRatio);
      }
      x = (width - viewWidth) / 2;
      y = (height - viewHeight) / 2;
  
      Log.d(TAG, "onSurfaceChanged w=" + width + " h=" + height);
      Log.d(TAG, " --&gt; x=" + x + " y=" + y + " gw=" + viewWidth + " gh=" + viewHeight);
  
      GLES20.glViewport(x, y, viewWidth, viewHeight);
  
      mViewportXoff = x;
      mViewportYoff = y;
      mViewportWidth = viewWidth;
      mViewportHeight = viewHeight;
  
  
      // Create an orthographic projection that maps the desired arena size to the viewport
      // dimensions.
      //
      // If we reversed {0, ARENA_HEIGHT} to {ARENA_HEIGHT, 0}, we'd have (0,0) in the
      // upper-left corner instead of the bottom left, which is more familiar for 2D
      // graphics work.  It might cause brain ache if we want to mix in 3D elements though.
      Matrix.orthoM(mProjectionMatrix, 0,  0, ARENA_WIDTH,
              0, ARENA_HEIGHT,  -1, 1);
  
      Log.d(TAG, "onSurfaceChangedEnd 1 w=" + width + " h=" + height);
  
      if (EXTRA_CHECK) Util.checkGlError("onSurfaceChanged end");
      Log.d(TAG, "onSurfaceEnded w=" + width + " h=" + height);
  }
  
  
  public void pause(){
      super.pause();
      this.recorder.gamePaused();
  }
  
  
  
  /**
   * Saves the current projection matrix and EGL state.
   */
  public void saveRenderState() {
      System.arraycopy(mProjectionMatrix, 0, mSavedMatrix, 0, mProjectionMatrix.length);
      mSavedEglDisplay = EGL14.eglGetCurrentDisplay();
      mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
      mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);
      mSavedEglContext = EGL14.eglGetCurrentContext();
  }
  
  /**
   * Saves the current projection matrix and EGL state.
   */
  public void restoreRenderState() {
      // switch back to previous state
      if (!EGL14.eglMakeCurrent(mSavedEglDisplay, mSavedEglDrawSurface, mSavedEglReadSurface,
              mSavedEglContext)) {
          throw new RuntimeException("eglMakeCurrent failed");
      }
      System.arraycopy(mSavedMatrix, 0, mProjectionMatrix, 0, mProjectionMatrix.length);
  }
  }
  </code></pre>
  
  <p>And then, i create a CustomUnityPlayerActivity to call this class</p>
  
  <pre><code>package com.example.screenrecorder;
  
  import android.content.res.Configuration;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.KeyEvent;
  import android.view.View;
  import android.view.Window;
  
  import com.unity3d.player.UnityPlayerActivity;
  
  public class CustomUnityActivity extends UnityPlayerActivity {
  
  private CustomUnityPlayer mUnityPlayer;
  private GameRecorder mRecorder;
  
  @Override
  protected void onCreate(Bundle paramBundle){
      Log.e("ScreenRecord","oncreate");
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      super.onCreate(paramBundle);
      this.mUnityPlayer = new CustomUnityPlayer(this);
      if (this.mUnityPlayer.getSettings().getBoolean("hide_status_bar", true))
        getWindow().setFlags(1024, 1024);
  
      int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
      boolean trueColor8888 = false;
      mUnityPlayer.init(glesMode, trueColor8888);
  
      View playerView = mUnityPlayer.getView();
      setContentView(playerView);
      playerView.requestFocus();
  
      this.mRecorder = GameRecorder.getInstance();
      this.mRecorder.prepareEncoder(this);
  }
  
  public void beginRecord(){
      Log.e("ScreenRecord","start record");
  
  
      this.mUnityPlayer.saveRenderState();
      this.mRecorder.firstTimeSetup();
      this.mRecorder.setStartRecord(true);
      this.mRecorder.makeCurrent();
      this.mUnityPlayer.restoreRenderState();
  }
  
  public void endRecord(){
      Log.e("ScreenRecord","end record");
      this.mRecorder.endRecord();
      this.mRecorder.setStartRecord(false);
      //this.mTransView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
  }
  
  public boolean isRecording(){
      return this.mRecorder.isRecording();
  }
  
  protected void onDestroy()
    {
      super.onDestroy();
      this.mUnityPlayer.quit();
    }
  
    protected void onPause()
    {
      super.onPause();
      this.mUnityPlayer.pause();
    }
  
    protected void onResume()
    {
      super.onResume();
      this.mUnityPlayer.resume();
    }
  
    public void onConfigurationChanged(Configuration paramConfiguration)
    {
      super.onConfigurationChanged(paramConfiguration);
      this.mUnityPlayer.configurationChanged(paramConfiguration);
    }
  
    public void onWindowFocusChanged(boolean paramBoolean)
    {
      super.onWindowFocusChanged(paramBoolean);
      this.mUnityPlayer.windowFocusChanged(paramBoolean);
    }
  
    public boolean onKeyDown(int paramInt, KeyEvent paramKeyEvent)
    {
      return this.mUnityPlayer.onKeyDown(paramInt, paramKeyEvent);
    }
  
    public boolean onKeyUp(int paramInt, KeyEvent paramKeyEvent)
    {
      return this.mUnityPlayer.onKeyUp(paramInt, paramKeyEvent);
    }
  }
  </code></pre>
  
  <p>My problem is that a video file is created successful but my game can't render anything.I read on <a href="http://bigflake.com/mediacodec/" rel="noreferrer">Android Media Codec sample</a> site and recognize that each frame would be render twice  (once for the display, once for the video) but I can't do this in Unity.Whenever I try to call super.onDrawFrame(gl) twice in onDrawFrame method , my game will be crashed.<br/>
  <br/>
  Any solution for my problem? Any help will be greatly appreciated!<br/>
  <br/>
  Thanks and best regard!<br/>
  <br/>
  Huy Tran </p>