Screen Video Record of Current Activity Android

EDIT: This answer is superceded by the answer below from Danpe.

Programmatically recording video from within your app will require root access. You'll notice that the apps available to do this in the Play Store prominently list "REQUIRES ROOT" in their app descriptions. You'll also notice that there may also be some specific hardware requirements for this approach to work ("Does not work on Galaxy Nexus or Tegra 2/3..." -- from the description of the Screencast Video Recorder app.

I have never written this code myself, but I've put together a very high level idea of the approach required. It appears from this post that you have to access the frame buffer data via "/dev/graphics/fb0". The access mode for the frame buffer is 660, which means that you need root access to get to it. Once you have root access, you can use the frame buffer data to create screen shots (this project might work for this task) and then create video from these screenshots (see this other SO question on how to create video from an image sequence).

I've used the Screencast app and it works well on a Samsung Note. I suspect that this is the basic approach they've taken.


Since Lollipop we can use the Media Projection API ! (API 21+)

Here is the following code that I use for recording, Note that we first need to get the user permissions for that ;)

private static final int CAST_PERMISSION_CODE = 22;
private DisplayMetrics mDisplayMetrics;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaRecorder mMediaRecorder;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mMediaRecorder = new MediaRecorder();

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

    getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);

    prepareRecording();
}

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 = createVirtualDisplay();
    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() {
    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*/);
}

This is no the final code but a GOOD base for start :)


Normal Android apps lack permission to capture the frame buffer (specifically, they aren't members of the AID_GRAPHICS group). This is for security reasons - otherwise they could snoop passwords etc from the soft keyboard. So in general you CANNOT capture the screen from an Android app without some way of getting around the privilege issue.

You CAN more-or-less capture a snapshot of the screen area currently taken up by your application by traversing to the top View in your view hierarchy and drawing it into a Bitmap using View.draw(Canvas), however this will not record the status bar, soft keyboard, OpenGL surfaces etc.

If you want to do better than this you will need to use an external tool. Tools either need root or to use the ADB interface, since processes started via the ADB interface have the AID_GRAPHICS privilege. Using the latter method a non-privileged app can connect to a privileged server to do the recording.

Roughly tools can be divided into the following categories:

  • Root-only framebuffer recorder apps (e.g. Screencast). These record directly from the /dev/graphics/fb0 device but only works on devices where the framebuffer is readable (e.g, not on the Tegra 2 Nexus 7).

  • Root-only screen capture recorder apps (e.g. SCR, Rec etc). These capture the screen via SurfaceFlinger and work on a much wider range of devices.

  • Non root screen capture recorder apps (e.g., Recordable, Ascrecorder). These require the user to enable USB debugging and start a daemon while connected via a host PC. Thereafter the host PC is not required until the device is rebooted. Can record audio as well.

  • ADB tools (e.g., the built-in screenrecorder on Android 4.4). Require you to be connected via a USB and can't capture audio.

For completeness there are also USB tools (e.g., Mobizen) that stream the screen across USB (limited by low USB bandwdith and can't record audio) and some devices can also transmit the screen over wifi, which can then captured on a separate device.