One of the new APIs introduced in Android 5.0 is the MediaProjection API. According to official documentation this API gives us the ability to capture screen contents with audio recording. As you may know screen cast is one of the new features introduced in Android. This Android MediaProjection API is the core API through which the screen cast takes place. By using this Android MediaProjection API we can also share screen over the network. This API also solves a very basic function which was needed in Android i.e. recording of screen programmatically. But sadly there are no samples available on how to do this. Therefore in this Capture and Record Android Screen using MediaProjection APIs tutorial, I would make an example where a video of device screen would be recorded, with audio.
Although Android media projection API solves the problem of recording and capturing device’s screen, but since it was introduced in lollipop version of android therefore as of now it cannot be used on API version lower than 21.
Android Media Projection Example : Record Screen Video by Code
To record our screen activity using Android media projection class we will be using MediaRecorder
class, which would actually record the screen activity. Android media recorder class is a simple class used for recording any type of audio and video. In this example we will be using the MediaRecorder
class to capture the output of MediaProjection
class once the capture intent is fired. But before doing so please add these permissions in your manifest.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
Please Note: If you don’t wish to record audio, then you don’t need to add the record audio permission. Only write external storage would be required.
Next, to use Android MediaProjection API lets make a layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="Recording Status: " android:textAppearance="?android:attr/textAppearanceLarge"/> <ToggleButton android:id="@+id/toggle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:layout_centerHorizontal="true" android:layout_marginTop="5dp" android:text="Start"/> <ImageView android:id="@+id/imageView" android:layout_width="150dp" android:layout_height="150dp" android:layout_below="@+id/toggle" android:layout_centerHorizontal="true" android:layout_marginTop="85dp" android:src="@mipmap/truiton_short"/> </RelativeLayout>
Please note: truiton_short
is just a drawable image, in your code, you may choose exclude it.
The above file would give a layout like this:
Next lets have a look at the main activity where screen recording would take place using the Android media projection APIs.
package com.truiton.screencapture; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; 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.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseIntArray; import android.view.Surface; import android.view.View; import android.widget.Toast; import android.widget.ToggleButton; import java.io.IOException; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int REQUEST_CODE = 1000; private int mScreenDensity; private MediaProjectionManager mProjectionManager; private static final int DISPLAY_WIDTH = 720; private static final int DISPLAY_HEIGHT = 1280; private MediaProjection mMediaProjection; private VirtualDisplay mVirtualDisplay; private MediaProjectionCallback mMediaProjectionCallback; private ToggleButton mToggleButton; private MediaRecorder mMediaRecorder; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); private static final int REQUEST_PERMISSIONS = 10; static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } @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(); mProjectionManager = (MediaProjectionManager) getSystemService (Context.MEDIA_PROJECTION_SERVICE); mToggleButton = (ToggleButton) findViewById(R.id.toggle); mToggleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + ContextCompat .checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) || ActivityCompat.shouldShowRequestPermissionRationale (MainActivity.this, Manifest.permission.RECORD_AUDIO)) { mToggleButton.setChecked(false); Snackbar.make(findViewById(android.R.id.content), R.string.label_permissions, Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission .WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS); } }).show(); } else { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission .WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, REQUEST_PERMISSIONS); } } else { onToggleScreenShare(v); } } }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != REQUEST_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; } mMediaProjectionCallback = new MediaProjectionCallback(); mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); mMediaProjection.registerCallback(mMediaProjectionCallback, null); mVirtualDisplay = createVirtualDisplay(); mMediaRecorder.start(); } public void onToggleScreenShare(View view) { if (((ToggleButton) view).isChecked()) { initRecorder(); shareScreen(); } else { mMediaRecorder.stop(); mMediaRecorder.reset(); Log.v(TAG, "Stopping Recording"); stopScreenSharing(); } } private void shareScreen() { if (mMediaProjection == null) { startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE); return; } mVirtualDisplay = createVirtualDisplay(); mMediaRecorder.start(); } 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 void initRecorder() { try { mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mMediaRecorder.setOutputFile(Environment .getExternalStoragePublicDirectory(Environment .DIRECTORY_DOWNLOADS) + "/video.mp4"); mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setVideoEncodingBitRate(512 * 1000); mMediaRecorder.setVideoFrameRate(30); int rotation = getWindowManager().getDefaultDisplay().getRotation(); int orientation = ORIENTATIONS.get(rotation + 90); mMediaRecorder.setOrientationHint(orientation); mMediaRecorder.prepare(); } catch (IOException e) { e.printStackTrace(); } } 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"); } mMediaProjection = null; stopScreenSharing(); } } private void stopScreenSharing() { if (mVirtualDisplay == null) { return; } mVirtualDisplay.release(); //mMediaRecorder.release(); //If used: mMediaRecorder object cannot // be reused again destroyMediaProjection(); } @Override public void onDestroy() { super.onDestroy(); destroyMediaProjection(); } private void destroyMediaProjection() { if (mMediaProjection != null) { mMediaProjection.unregisterCallback(mMediaProjectionCallback); mMediaProjection.stop(); mMediaProjection = null; } Log.i(TAG, "MediaProjection Stopped"); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_PERMISSIONS: { if ((grantResults.length > 0) && (grantResults[0] + grantResults[1]) == PackageManager.PERMISSION_GRANTED) { onToggleScreenShare(mToggleButton); } else { mToggleButton.setChecked(false); Snackbar.make(findViewById(android.R.id.content), R.string.label_permissions, Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse("package:" + getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); startActivity(intent); } }).show(); } return; } } } }
As you may see above, to start a media projection first an intent is created using createScreenCaptureIntent()
method of MediaProjectionManager class. When user approves the screen cast request, media projection is started from the onActivityResult
method, along with the media recording. By doing this, we are allowing the app to start a screen cast which is recorded on the device itself. This would generate an audio/video file in mp4 format on your SD card. Although if you don’t wish to record audio, you can can remove the setAudioSource
and setAudioEncoder
lines from the initRecorder()
method. To view the full source code please visit the repo below:
The place where both Android MediaProjection class and MediaRecording class interact is when creating a virtual display using the createVirtualDisplay
method. This method basically creates a virtual display where the screen contents are buffered on to a Surface. To capture the screen contents in a MediaRecorder
, I initialized a media recorder above with video source as a surface using the MediaRecorder.VideoSource.SURFACE
parameter. Then in the createVirtualDisplay
method I specified the surface of media recorder by using mMediaRecorder.getSurface()
method. By doing this I gave the input to media recorder from the media projection class in real time, through which a screen capture video recording file is generated. Hope this helps. Connect with us on Facebook, Twitter, and Google+.
Born in New Delhi, India. A software engineer by profession, an android enthusiast and an evangelist. My motive here is to create a group of skilled developers, who can develop something new and good. Reason being programming is my passion, and also it feels good to make a device do something you want. In a very short span of time professionally I have worked with many tech firms. As of now too, I am employed as a senior engineer in a leading tech company. In total I may have worked on more than 20 projects professionally, and whenever I get spare time I share my thoughts here at Truiton.
Thanks for this code. I tried it on an emulator with Android 5.1 but the mp4 file is blank… Did I miss something?
Thanks for the article!!! This is amazing and I learned a lot. Do you know how to capture system audio as well? Actually what I’m trying to do is capturing screen and sound at the same time but I’m not sure that’s possible without rooting.
Same problem. Any improvements?
how to stop and resume the video Recorder like AZ screen recorder?
Hello Mohit,
Thanks for this nice tutorial, actually i m doing work on the app that can share my screen to another android device so can i achieve this functionality using this code?
Hi,
were you able to achieve this ? i am also looking for sharing screen in android app
Hi above code is not working on android M , Fatal error is coming at mediarecorder.stop due to illegalstate exception.
can you please check and let me know the solution.
above code is working fine for Android L , but for android M its not working .
its giving fatal error in mediarecorder.stop
can you please check and provide some solution ?
working for M as well.
Post Updated. Should work now !
Sir,currently I am trying to capture the contents of android phone (even at background) and stream them.Will it be possible.?
Yes
When I run the Code and Click on ToggleButton it restarts my phone(Moto E).
Have problem setting up the audio in mediaRecorder (setAudioSource failed.). I commented out the audio code, but now it crashes when I press the toggle button.
Phone used : Nexus 5 with Android M
Error: failed to get surface in “createVirtualDisplay()” method.
Tested again on Android 5.1.1 the code works well. It only crashes when I try it on Android 6.0.1
Please try once again with updated code. I added permission checks for Android M.
How to achieve this on service?
There is an issue while putting this as a service
Once again post updated. Added permission checks.
It should solve the Android M crashes.
Thank you for the excellent code.
I can run it on Android 5.1. But the frame rate of the recorded video is less than 1 frame per second. Any idea? Thanks.
on my phone, it says failed to get surface 🙁 what to do.
Hello nice tutorial..
But is it possible to directly grant permission without asking every time while screen recording?
can we add .mp3 file as audio source
Hi Mohit , Nice tutorial ..
Can you let me know how to share the captured screen to another android device ?
Could you explain a bit about how the orientation is set, can we use it for both landscape and Portrait mode
Hi Mohit I am using this same code…
Its working fine on most of the devices but crashing on few device of android 6.0
mainly in MI-3S PRIME
Hi,
Can you please show a sample of how to use this for a single screenshot ? Or one after another, by demand?
Hi,
Your post is great, but one issue:
Your solution works fabulously on Android 6 & Android 7, but on Android 5 it records only the voice. I’m working on Genymotion emulators.
Regards,
Harlan
please use an actual device.
Hi Mohit,
Its very useful code in my scenario. But I would like to ask one more question is that it is possible to record screen on particular area not whole screen.
Thanks in advance.
Regards
Raj K
Hi,
You think it is possible to getframe to capture bitmap during video recorder time ?
Thx for your post ! 😉
Thank you very much for the tutorial. It helped me a lot.
Do you know how to improve the quality of the video taken? It looks very pixelated.
you can set the
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
to greater values
also can use 7000000
Thank you for Tutorial. How can I achieve Pause and Resume screen capturing using this?
Hello can i ask if i can share the screen recording in real time via wi-fi direct with multiple android devices?
When i click stop, the app force closes for android 7.0, but when i try it for android 6.0 it’s working
hey and thanks for nice piece of code
can we use it in a service class and if yes
how?
thanks again for your code
the code works fine, how ever there is an issue,
the problem is if we start recording in portrait mode , the video starts recording the full screen, how ever while we are recording, if we change orientation to landscape the width and height of video remains same, but the screen recorded is smaller and remaining space is taken by black screen. There are some apps which continue to change the width and height on the go even when orientation is changed, and record the whole screen.
So the question is how can we maintain the screen recording to whole screen and not cause the black screen, without stopping the recording?
Hi pawaom! Were you able to solve this? I found a repo somewhere that does it but It doesn’t seem to record the MIC audio (a feature I need). Cheers!
can you share the link
Hi. Why is the file empty? 0 byte. It is blank. Did I miss something?
On android 7.0
Thank you very Much Sir for sharing the codes ..
My question is where does the recorded file goes ?
Because I cant find them on my mobile phone .. Thank you …
The file goes here:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/video.mp4"
How do I make it show up in the gallery immediately after recording?
Hello Mohit,Can you help me to live stream video fusing screen capture display.I want to live stream my display
Thanks for tutorial. How can we exclude some of the elements such as record button from the output video.
Hello Sir
The above code is working perfectly for screen recording.
But how to record only a single relative layout (which is holding some child views) ?
Thanks..
Hi Mohit! I run into your code for something very similar I’m trying to do. Have you tried using this code to record the screen on landscape mode? It records, but it adds black padding on top and bottom…making the whole recording on landscape mode pointless. I don’t know if it’s a virtual display thing, a media recorder orientation hint thing (have tried varying both) but I can’t seem to get it right. Any thoughts? Cheers!
Is there any tutorial how to cast a stream for the screen instead of recording ? , like sending it as rtsp or tcp or udp frames to a receiver such omxplayer or ffmpeg?
if there is . please contact me and i’ll donate for your activity
thank you for your code but how to modify it little like start recording on activity pause and start on resume can you plz help me out in this regard ?