Java.lang.IllegalStateException: no se pudo obtener la superficie

Estoy tratando de crear una aplicación que permite al usuario grabar la pantalla de sus teléfonos inteligentes. Este es mi código inicial:

import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.MediaRecorder; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.widget.Button; import android.widget.Toast; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class MainActivity extends AppCompatActivity { private static final int CAST_PERMISSION_CODE = 22; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); private MediaProjection mMediaProjection; private VirtualDisplay mVirtualDisplay; private MediaRecorder mMediaRecorder; private MediaProjectionManager mProjectionManager; private Button startButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startButton = (Button) findViewById( R.id.recordButton ); mMediaRecorder = new MediaRecorder(); mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); getWindowManager().getDefaultDisplay().getMetrics(this.mDisplayMetrics); prepareRecording(); startRecording(); } private void startRecording() { // If mMediaProjection is null that means we didn't get a context, lets ask the user if (mMediaProjection == null) { // This asks for user permissions to capture the screen startActivityForResult(mProjectionManager.createScreenCaptureIntent(), CAST_PERMISSION_CODE); return; } mVirtualDisplay = getVirtualDisplay(); mMediaRecorder.start(); } private void stopRecording() { if (mMediaRecorder != null) { mMediaRecorder.stop(); mMediaRecorder.reset(); } if (mVirtualDisplay != null) { mVirtualDisplay.release(); } if (mMediaProjection != null) { mMediaProjection.stop(); } prepareRecording(); } public String getCurSysDate() { return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date()); } private void prepareRecording() { try { mMediaRecorder.prepare(); } catch (Exception e) { e.printStackTrace(); return; } final String directory = Environment.getExternalStorageDirectory() + File.separator + "Recordings"; if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { Toast.makeText(this, "Failed to get External Storage", Toast.LENGTH_SHORT).show(); return; } final File folder = new File(directory); boolean success = true; if (!folder.exists()) { success = folder.mkdir(); } String filePath; if (success) { String videoName = ("capture_" + getCurSysDate() + ".mp4"); filePath = directory + File.separator + videoName; } else { Toast.makeText(this, "Failed to create Recordings directory", Toast.LENGTH_SHORT).show(); return; } int width = mDisplayMetrics.widthPixels; int height = mDisplayMetrics.heightPixels; mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setVideoEncodingBitRate(512 * 1000); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.setVideoSize(width, height); mMediaRecorder.setOutputFile(filePath); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != CAST_PERMISSION_CODE) { // Where did we get this request from ? -_- //Log.w(TAG, "Unknown request code: " + requestCode); return; } if (resultCode != RESULT_OK) { Toast.makeText(this, "Screen Cast Permission Denied :(", Toast.LENGTH_SHORT).show(); return; } mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); // TODO Register a callback that will listen onStop and release & prepare the recorder for next recording // mMediaProjection.registerCallback(callback, null); mVirtualDisplay = getVirtualDisplay(); mMediaRecorder.start(); } private VirtualDisplay getVirtualDisplay() { int screenDensity = mDisplayMetrics.densityDpi; int width = mDisplayMetrics.widthPixels; int height = mDisplayMetrics.heightPixels; return mMediaProjection.createVirtualDisplay(this.getClass().getSimpleName(), width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null /*Callbacks*/, null /*Handler*/); } } 

Después de mostrar un mensaje que informa al usuario sobre la función de captura de pantalla, mi app aplastará.

 java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=22, result=-1, data=Intent { (has extras) }} to activity {gr.awm.clrecorder/gr.awm.clrecorder.MainActivity}: java.lang.IllegalStateException: failed to get surface at android.app.ActivityThread.deliverResults(ActivityThread.java:3974) at android.app.ActivityThread.handleSendResult(ActivityThread.java:4017) at android.app.ActivityThread.access$1400(ActivityThread.java:172) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1471) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:145) at android.app.ActivityThread.main(ActivityThread.java:5832) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194) Caused by: java.lang.IllegalStateException: failed to get surface at android.media.MediaRecorder.getSurface(Native Method) at gr.awm.clrecorder.MainActivity.getVirtualDisplay(MainActivity.java:148) at gr.awm.clrecorder.MainActivity.onActivityResult(MainActivity.java:135) 

¿Hay alguna manera de resolver este problema? Cualquier consejo sería útil y profundamente apreciado. Gracias por adelantado

No importa el comentario btw.

Cavé en la documentación y su código y obtuvo los siguientes resultados.

Este es el orden que usted llama los métodos mMediaRecorder para obtener una superficie.

 mMediaRecorder.prepare(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(Me diaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setVideoEncodingBitRate(512 * 1000); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.setVideoSize(width, height); mMediaRecorder.setOutputFile(filePath); 

Esto es lo que dice la documentación

 //Call this method before prepare(). setVideoEncodingBitRate(); //no exception thrown //Must be called after setVideoSource(). Call this after setOutFormat() but before prepare(). setVideoSize(width, height); //IllegalStateException if it is called after prepare() or before setOutputFormat() //Call this only before setOutputFormat(). setAudioSource(); //IllegalStateException if it is called after setOutputFormat() setVideoSource(); //IllegalStateException if it is called after setOutputFormat() //Call this after setOutputFormat() and before prepare(). setVideoEncoder(); //IllegalStateException if it is called before setOutputFormat() or after prepare() setAudioEncoder(); //IllegalStateException if it is called before setOutputFormat() or after prepare(). //Call this after setAudioSource()/setVideoSource() but before prepare(). setOutputFormat(); //IllegalStateException if it is called after prepare() or before setAudioSource()/setVideoSource(). //Call this after setOutputFormat() but before prepare(). setOutputFile(); //IllegalStateException if it is called before setOutputFormat() or after prepare() //Must be called after setVideoSource(). Call this after setOutFormat() but before prepare(). setVideoFrameRate(); //IllegalStateException if it is called after prepare() or before setOutputFormat(). //This method must be called after setting up the desired audio and video sources, encoders, file format, etc., but before start() prepare() //IllegalStateException if it is called after start() or before setOutputFormat(). 

Así que para obtener el mMediaRecorder en un estado correcto, tiene que llamar a los métodos en este orden:

 setAudioSource setVideoSource setOutputFormat setAudioEncoder setVideoEncoder setVideoSize setVideoFrameRate setOutputFile setVideoEncodingBitRate prepare start 

Creo que también recibí un error indocumentado cuando llamé a los métodos de encoder Set antes de que el conjunto de métodos de origen

Edit: Pensé que conseguí el código de trabajo, pero todavía consigo IllegalStateExceptions aunque el código está en el orden de la documentación.

Edit2: Tengo que trabajar ahora. Cosas que también podrían no funcionar y mensajes de error adicionales:

  • Permisos para almacenamiento externo y micrófono no establecidos (agregue permisos de uso en el manifiesto)
  • El inicio de Android MediaRecorder falló en estado no válido 4
  • IllegalStateException [inicio llamado en un estado no válido: 1] al reiniciar Android MediaRecorder

Tuve que crear un directorio donde la aplicación podría escribir. No pude conseguir el almacenaje externo para trabajar así que utilicé el directorio de los datos. Pero eso no está relacionado con el código mMediaRecorder

Este código funciona:

 private void prepareRecording() { //Deal with FileDescriptor and Directory here //Took audio out because emulator has no mic //mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setVideoEncodingBitRate(512 * 1000); //mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoSize(width, height); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.setOutputFile(filePath); try { mMediaRecorder.prepare(); } catch (Exception e) { e.printStackTrace(); return; } //Field variable to hold surface object //Deal with it as you see fit surface = mMediaRecorder.getSurface(); 

Tenga en cuenta Aunque el código anterior funciona para crear el MediaRecorder correctamente y escribir en el almacenamiento, se bloquea todo el emulador cuando se llama a mMediaRecorder.stop() .

  • Reproductor de música Android no reindexando después de la adición de canción
  • Android analizando la cadena hasta la fecha con SimpleDateFormat
  • AsyncTask: onPostExecute se ejecuta dos veces?
  • Java.lang.verifyerror ¿cómo puedo arreglar o incluso averiguar la causa raíz?
  • Comercio con método de declaración sincronizado en java?
  • Android: startActivityForResult () con funcionalidad de botón BACK
  • Lazy Loading no funciona correctamente
  • AlarmManager efecto en la batería de una aplicación de reloj despertador
  • Kryonet y Android
  • Android Web Scraping con un navegador sin cabeza
  • Java / Android: Lectura / escritura de una matriz de bytes sobre un socket
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.