MediaProjection: How to record your Android device

For a recent project I tasked myself with finding a way to let the user record what they were seeing on the device. The nuts and bolts of the solution can be identified with two classes:

  • MediaProjection was introduced in Lollipop and gives us the ability to capture what’s on the screen.
  • MediaRecorder is pretty self explanatory, this class simplifies recording audio and video.

Requirements

Accepting

The first step is to request permission to capture the screen.

val manager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(manager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION)

Launching this intent will show a confirmation dialog to the user which allows them to authorise the screen capturing.

Once they accept, you can use the result code and data intent returned in onActivityResult, we are able to create a MediaProjection.

projection = manager.getMediaProjection(resultCode, data) as MediaProjection

Recording

Next we need to define our MediaRecorder. I’m not too fussed about the specific details so I used the predefined high quality CamcorderProfile but you can create your own and specify everything from output format to bits per second. One slightly different thing is that I defined the height and width using the Window’s DisplayMetrics so it exactly matched the screen size.

mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT)
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
val profile: CamcorderProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)
profile.videoFrameHeight = metrics.heightPixels
profile.videoFrameWidth = metrics.widthPixels
mMediaRecorder.setProfile(profile)
mMediaRecorder.setOutputFile(filename)
mMediaRecorder.prepare()

One gotcha about the MediaRecorder is that the methods have to be defined in the correct order. For example, once setting the Camcorder Profile, you can’t change the AudioSource without crashing. The javadoc has more details to the state machine behind this.

Mirroring

Once we have the MediaRecorder and MediaProjection, we can create a virtual display that mirrors the projection and then bind it to the MediaRecorder’s SurfaceView. If you weren’t interested in the recording aspect of this demo, you could bind to your own SurfaceView.

mVirtualDisplay = mMediaProjection!!.createVirtualDisplay(VIRTUAL_DISPLAY_NAME,
metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder.surface, null, null)

Now everything is set up, tell the MediaRecorder to start() recording and you’ll notice the cast icon appear in your phone’s status bar.

When you’re ready, call stop(). It’s also important to release the MediaProjection and VirtualDisplay too. The recording will then be saved to the location you define in setOutputFile.

mMediaRecorder?.stop()
mMediaProjection?.stop()
mVirtualDisplay?.release()

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.