-1

I'm trying to record phone screen in Android 5.0 Lollipop using MediaProjection and to save recorded video.

I tried this code, but after I added saveFile(uri) to onActivityResult the app crashes when I start recording.

What is the right way to get recorded video and to save it?

   public class MainActivity extends Activity {
        private static final String TAG = "MediaProjectionDemo";
        private static final int PERMISSION_CODE = 1;
        private static final List<Resolution> RESOLUTIONS = new ArrayList<Resolution>() {{
            add(new Resolution(640,360));
            add(new Resolution(960,540));
            add(new Resolution(1366,768));
            add(new Resolution(1600,900));
        }};

        private int mScreenDensity;
        private MediaProjectionManager mProjectionManager;

        private int mDisplayWidth;
        private int mDisplayHeight;
        private boolean mScreenSharing;

        private MediaProjection mMediaProjection;
        private VirtualDisplay mVirtualDisplay;
        private Surface mSurface;
        private SurfaceView mSurfaceView;

        @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;

            mSurfaceView = (SurfaceView) findViewById(R.id.surface);
            mSurface = mSurfaceView.getHolder().getSurface();
            mProjectionManager =
                    (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);

            ArrayAdapter<Resolution> arrayAdapter = new ArrayAdapter<Resolution>(
                    this, android.R.layout.simple_list_item_1, RESOLUTIONS);
            Spinner s = (Spinner) findViewById(R.id.spinner);
            s.setAdapter(arrayAdapter);
            s.setOnItemSelectedListener(new ResolutionSelector());
            s.setSelection(0);
        }

        @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,
                        "User denied screen sharing permission", Toast.LENGTH_SHORT).show();
                return;
            }

            try {
                java.net.URI uri = new java.net.URI(data.getData().toString());
                saveFile(uri);

            } catch (URISyntaxException e) {
                e.printStackTrace();
            }

            mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
            mVirtualDisplay = createVirtualDisplay();
        }

        void saveFile(URI sourceUri)
        {
            String sourceFilename= sourceUri.getPath();
            String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath()+ File.separatorChar+"abc.mp4";

            BufferedInputStream bis = null;
            BufferedOutputStream bos = null;

            try {
                bis = new BufferedInputStream(new FileInputStream(sourceFilename));
                bos = new BufferedOutputStream(new FileOutputStream(destinationFilename, false));
                byte[] buf = new byte[1024];
                bis.read(buf);
                do {
                    bos.write(buf);
                } while(bis.read(buf) != -1);
            } catch (IOException e) {

            } finally {
                try {
                    if (bis != null) bis.close();
                    if (bos != null) bos.close();
                } catch (IOException e) {

                }
            }
        }

        public void onToggleScreenShare(View view) {
            if (((ToggleButton) view).isChecked()) {
                shareScreen();
            } else {
                stopScreenSharing();
            }
        }

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

        private void stopScreenSharing() {
            mScreenSharing = false;
            if (mVirtualDisplay == null) {
                return;
            }
            mVirtualDisplay.release();
        }

        private VirtualDisplay createVirtualDisplay() {
            return mMediaProjection.createVirtualDisplay("ScreenSharingDemo",
                    mDisplayWidth, mDisplayHeight, mScreenDensity,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                    mSurface, null /*Callbacks*/, null /*Handler*/);
        }

        private void resizeVirtualDisplay() {
            if (mVirtualDisplay == null) {
                return;
            }
            mVirtualDisplay.resize(mDisplayWidth, mDisplayHeight, mScreenDensity);
        }

        private class ResolutionSelector implements Spinner.OnItemSelectedListener {
            @Override
            public void onItemSelected(AdapterView<?> parent, View v, int pos, long id) {
                Resolution r = (Resolution) parent.getItemAtPosition(pos);
                ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
                if (getResources().getConfiguration().orientation
                        == Configuration.ORIENTATION_LANDSCAPE) {
                    mDisplayHeight = r.y;
                    mDisplayWidth = r.x;
                } else {
                    mDisplayHeight = r.x;
                    mDisplayWidth = r.y;
                }
                lp.height = mDisplayHeight;
                lp.width = mDisplayWidth;
                mSurfaceView.setLayoutParams(lp);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) { /* Ignore */ }
        }

        private class MediaProjectionCallback extends MediaProjection.Callback {
            @Override
            public void onStop() {
                mMediaProjection = null;
                stopScreenSharing();
            }
        }

        private class SurfaceCallbacks implements SurfaceHolder.Callback {
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                mDisplayWidth = width;
                mDisplayHeight = height;
                resizeVirtualDisplay();
            }

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                mSurface = holder.getSurface();
                if (mScreenSharing) {
                    shareScreen();
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (!mScreenSharing) {
                    stopScreenSharing();
                }
            }
        }

        private static class Resolution {
            int x;
            int y;

            public Resolution(int x, int y) {
                this.x = x;
                this.y = y;
            }

            @Override
            public String toString() {
                return x + "x" + y;
            }
        }
    }

logcat:

    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.name.name/com.name.name.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
            at android.app.ActivityThread.deliverResults(ActivityThread.java:3539)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3582)
            at android.app.ActivityThread.access$1300(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1327)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            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:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
            at com.name.name.MainActivity.onActivityResult(MainActivity.java:103) // java.net.URI uri = new java.net.URI(data.getData().toString());
            at android.app.Activity.dispatchActivityResult(Activity.java:6135)
            at android.app.ActivityThread.deliverResults(ActivityThread.java:3535)
            at android.app.ActivityThread.handleSendResult(ActivityThread.java:3582)
            at android.app.ActivityThread.access$1300(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1327)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            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:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
fadden
  • 51,356
  • 5
  • 116
  • 166
user3051755
  • 1,497
  • 2
  • 16
  • 27

1 Answers1

0

When you call startActivityOnResult, it shows you a confirmation dialog on the device. Once you react to this dialog, onActivityResult is called. If resultCode == RESULT_OK, it doesn't mean that you started recording, it just means that you have been granted the permission to capture the screen (Mirror the screen into your Activity).

What you need to do now is to start recording the video from the surface you already have in your activity using MediaRecorder.

Community
  • 1
  • 1
MohanadMohie
  • 1,033
  • 1
  • 10
  • 17