8

I try to test a sample code about screen record from below link, I modified some code to disable recording audio.

http://www.truiton.com/2015/05/capture-record-android-screen-using-mediaprojection-apis/

I test the code in Android Studio V1.3, but I get the following error, and the file capture.mp4 is blank.

I'm not sure whether I must test the code in real mobile phone? Could you help me ? Thanks!

Error Info

09-22 06:41:50.250    2167-2167/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: screencapture.truiton.com.myapplication, PID: 2167
    java.lang.RuntimeException: stop failed.
            at android.media.MediaRecorder.stop(Native Method)
            at screencapture.truiton.com.myapplication.MainActivity.onToggleScreenShare(MainActivity.java:93)
            at screencapture.truiton.com.myapplication.MainActivity$1.onClick(MainActivity.java:55)
            at android.view.View.performClick(View.java:4780)
            at android.widget.CompoundButton.performClick(CompoundButton.java:120)
            at android.view.View$PerformClick.run(View.java:19866)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5257)
            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:903)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Source Code

package screencapture.truiton.com.myapplication;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";
    private static final int PERMISSION_CODE = 1;
    private int mScreenDensity;
    private MediaProjectionManager mProjectionManager;
    private static final int DISPLAY_WIDTH = 480;
    private static final int DISPLAY_HEIGHT = 640;
    private MediaProjection mMediaProjection;
    private VirtualDisplay mVirtualDisplay;
    private MediaProjectionCallback mMediaProjectionCallback;
    private ToggleButton mToggleButton;
    private MediaRecorder mMediaRecorder;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        mScreenDensity = metrics.densityDpi;

        mMediaRecorder = new MediaRecorder();
        initRecorder();
        prepareRecorder();

        mProjectionManager = (MediaProjectionManager) getSystemService
                (Context.MEDIA_PROJECTION_SERVICE);

        mToggleButton = (ToggleButton) findViewById(R.id.toggle);
        mToggleButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onToggleScreenShare(v);
            }
        });

        mMediaProjectionCallback = new MediaProjectionCallback();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mMediaProjection != null) {
            mMediaProjection.stop();
            mMediaProjection = null;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode != PERMISSION_CODE) {
            Log.e(TAG, "Unknown request code: " + requestCode);
            return;
        }
        if (resultCode != RESULT_OK) {
            Toast.makeText(this,
                    "Screen Cast Permission Denied", Toast.LENGTH_SHORT).show();
            mToggleButton.setChecked(false);
            return;
        }
        mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
        mMediaProjection.registerCallback(mMediaProjectionCallback, null);
        mVirtualDisplay = createVirtualDisplay();
        mMediaRecorder.start();
    }

    public void onToggleScreenShare(View view) {
        if (((ToggleButton) view).isChecked()) {
            shareScreen();
        } else {
            mMediaRecorder.stop();
            mMediaRecorder.reset();
            Log.v(TAG, "Recording Stopped");
            stopScreenSharing();
            initRecorder();
            prepareRecorder();
        }
    }

    private void shareScreen() {
        if (mMediaProjection == null) {
            startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE);
            return;
        }
        mVirtualDisplay = createVirtualDisplay();
        mMediaRecorder.start();
    }

    private void stopScreenSharing() {
        if (mVirtualDisplay == null) {
            return;
        }
        mVirtualDisplay.release();
        //mMediaRecorder.release();
    }

    private VirtualDisplay createVirtualDisplay() {
        return mMediaProjection.createVirtualDisplay("MainActivity",
                DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mMediaRecorder.getSurface(), null /*Callbacks*/, null /*Handler*/);
    }

    private class MediaProjectionCallback extends MediaProjection.Callback {
        @Override
        public void onStop() {
            if (mToggleButton.isChecked()) {
                mToggleButton.setChecked(false);
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                Log.v(TAG, "Recording Stopped");
                initRecorder();
                prepareRecorder();
            }
            mMediaProjection = null;
            stopScreenSharing();
            Log.i(TAG, "MediaProjection Stopped");
        }
    }

    private void prepareRecorder() {
        try {
            mMediaRecorder.prepare();
        } catch (IllegalStateException e) {
            e.printStackTrace();
            finish();
        } catch (IOException e) {
            e.printStackTrace();
            finish();
        }
    }

    private void initRecorder() {

        //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(DISPLAY_WIDTH, DISPLAY_HEIGHT);
        mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory() + "/capture.mp4");
    }
}
Pragnesh Ghoda シ
  • 8,318
  • 3
  • 25
  • 40
HelloCW
  • 843
  • 22
  • 125
  • 310
  • See: http://stackoverflow.com/questions/26545970/take-a-screenshot-using-mediaprojection – Morrison Chang Sep 22 '15 at 07:14
  • why don't you try running it in device. at least you will discover something more – karan Sep 22 '15 at 07:26
  • Sorry, I have no android device with Android 5.0 installed, can I test screen record in emulator ? Thanks! – HelloCW Sep 22 '15 at 08:01
  • To Morrison Chang, I have read http://stackoverflow.com/questions/26545970/take-a-screenshot-using-mediaprojection, but I still understand that why the sample code cause runtime error. Is there some error code in the sample? Thanks! – HelloCW Sep 22 '15 at 08:10
  • screen recording does not work in emulator with option provided in android studio. I think then it will not work programmatically as well. try using a device. – Rahul Tiwari Sep 25 '15 at 07:43
  • Thanks! To Rahul Tiwari : What does mean "I think then it will not work programmatically as well" ? – HelloCW Sep 25 '15 at 08:28
  • Where can I find "screen recording does not work in emulator with option provided" in official document? Thanks! – HelloCW Sep 25 '15 at 08:30
  • @HelloCW I said that with personal experience. Using screen record option in Android DDMS tab of android studio I am able to capture video of devices but the same thing does not work for emulators and generates a 0 byte mp4 file – Rahul Tiwari Sep 25 '15 at 08:45
  • @HelloCW are you using host GPU option in your emulator? – Rahul Tiwari Sep 29 '15 at 13:49
  • Yes, I used host GPU option in your emulator – HelloCW Sep 30 '15 at 03:15
  • you better use [genymotion](https://www.genymotion.com/) ! It's 200x better than regular emulator. – Saehun Sean Oh Sep 30 '15 at 17:14

1 Answers1

1

From my experience, Android emulators do allow reading from surfaces (which is the only thing really required for recording) but fail to encode video (and sometimes take screenshots) due to bugs/limitations in framework Java code. There are two primary reasons:

  • The emulator may not declare support for EGL_ANDROID_recordable extension, which is demanded by MediaRecorder/MediaCodec. You can check for the extension support by executing following command:

    adb shell dumpsys SurfaceFlinger | grep EGL_ANDROID_recordable
    
  • The only color format, supported by Android 6 builtin software encoder, is YUV. Video buffers, coming from emulator GPU, are likely to have different format.

Note, that above limitations aren't exclusive to emulator and may cause failures on actual devices as well. You can work around them by doing format conversion in software (on CPU or via GLES), using approach outlines in this answer, but the performance of software encoder is going to be abysmal either way.

Community
  • 1
  • 1
user1643723
  • 4,109
  • 1
  • 24
  • 48