Screen Recorder Plugin para Android en Unity

Estoy desarrollando un plugin Unity-Android para grabar la pantalla del juego y crear un archivo de video mp4. Seguiré a Android Breakout en este sitio: http://bigflake.com/mediacodec/ .
Primero, creo mi clase de CustomUnityPlayer que extiende la clase de UnityPlayer y anula el método de onDrawFrame. Aquí está mi código de la clase de CustomUnityPlayer:

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 & 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() && 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, eg 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, eg 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 (eg 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 > (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, " --> 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); } } 

Y luego, creo un CustomUnityPlayerActivity para llamar a esta clase

 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); } } 

Mi problema es que un archivo de video se crea con éxito, pero mi juego no puede hacer nada. Leo en el sitio de ejemplo de Android Media Codec y reconozco que cada marco sería renderizar dos veces (una vez para la pantalla, una vez para el video), pero puedo 'T hacer esto en Unity.Whenever trato de llamar a super.onDrawFrame (gl) dos veces en el método onDrawFrame, mi juego se estrelló.

¿Alguna solución para mi problema? ¡Cualquier ayuda será apreciada!

¡Gracias y un cordial saludo!

Huy Tran

Kamcord plug-in puede ayudarle: http://www.kamcord.com/

Por último, utilizar un FrameBufferObject (FBO) para hacer offscreen y obtener su textura vinculante para blit dos veces:

  • Renderizar a la superficie de vídeo
  • Redibujar a la pantalla del dispositivo

Puede encontrar más detalles sobre esta solución por referencia a mi otra pregunta uso FBO para grabar Unity juegos

  • Multi-plataforma 3d-Engine para incrustar en iOS / Android-View?
  • UnitySendMessage no funciona de la actividad no-unidad
  • Soporta múltiples proporciones en Unity
  • Texturas negras en Unity android al crear una compilación de desarrollo con depuración de secuencias de comandos
  • El proyecto Build Unity con Jenkins falló
  • ¿Cómo mostrar la misma parte del juego en cada relación de aspecto en dispositivos Android con Unity?
  • Cómo hacer la animación 2D en Unity
  • ¿Cómo abrir la actividad transparente de UnityPlayerActivity?
  • Error de segmentación OSX 11 - Android
  • Mostrar Unity Scene como Sub View en el estudio android
  • Google Play Services no muestra la tabla de clasificación después de Auth
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.