Android4.4开始支持录制屏幕,但首先需要获取Root权限才可以运行。
Android5.0及之后Android API 开放了视频录制的接口,其实严格来说,是屏幕采集的接口,也就是 MediaProjection 和 MediaProjectionManager。该篇文章是基于该方法进行的录制。
public static void checkPermission(AppCompatActivity activity) {
if (Build.VERSION.SDK_INT >= 23) {
int checkPermission =
ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_PHONE_STATE)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
//动态申请
ActivityCompat.requestPermissions(activity, new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 123);
}
}
}
MediaProjectionManager
onActivityResult
拿到是否允许的结果Service
进行录制屏幕该类代码如下
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.screen.recorder.demo.service.ScreenRecordService;
/**
* @author by talon, Date on 19/6/23.
* note:
*/
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 1;
private MediaProjectionManager mMediaProjectionManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkPermission(this); //检查权限
mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
}
public void StartRecorder(View view) {
createScreenCapture();
}
public void StopRecorder(View view){
Intent service = new Intent(this, ScreenRecordService.class);
stopService(service);
}
public static void checkPermission(AppCompatActivity activity) {
if (Build.VERSION.SDK_INT >= 23) {
int checkPermission =
ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_PHONE_STATE)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
if (checkPermission != PackageManager.PERMISSION_GRANTED) {
//动态申请
ActivityCompat.requestPermissions(activity, new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 123);
}
}
}
private void createScreenCapture() {
Intent captureIntent = mMediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
try {
Toast.makeText(this, "允许录屏", Toast.LENGTH_SHORT).show();
Intent service = new Intent(this, ScreenRecordService.class);
service.putExtra("resultCode", resultCode);
service.putExtra("data", data);
startService(service);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(this, "拒绝录屏", Toast.LENGTH_SHORT).show();
}
}
}
对应的视图代码如下
onActivityResult
回调产生的resultCode
与data
dpi
MediaProjection
MediaRecorder
VirtualDisplay
createMediaRecorder
方法中配置保存视频的信息,文件的路径、文件名、清晰度等该类代码如下
import android.app.Service;
import android.content.Context;
import android.content.Intent;
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.os.Environment;
import android.os.IBinder;
import android.util.Log;
import com.screen.recorder.demo.utils.ScreenUtils;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author by talon, Date on 19/6/23.
* note:
*/
public class ScreenRecordService extends Service {
private final String TAG = "ScreenRecordService";
/**
* 是否为标清视频
*/
private boolean isVideoSd = false;
private int mScreenWidth;
private int mScreenHeight;
private int mScreenDensity;
private int mResultCode;
private Intent mResultData;
private MediaProjection mMediaProjection;
private MediaRecorder mMediaRecorder;
private VirtualDisplay mVirtualDisplay;
public ScreenRecordService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mResultCode = intent.getIntExtra("resultCode", 1);
mResultData = intent.getParcelableExtra("data");
getScreenBaseInfo();
mMediaProjection = createMediaProjection();
mMediaRecorder = createMediaRecorder();
mVirtualDisplay = createVirtualDisplay(); // 必须在mediaRecorder.prepare() 之后调用,否则报错"fail to get surface"
mMediaRecorder.start();
return Service.START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
if(mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
if(mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
mMediaProjection.stop();
mMediaRecorder.reset();
}
if(mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
}
/**
* 获取屏幕相关数据
*/
private void getScreenBaseInfo() {
mScreenWidth = ScreenUtils.getScreenWidth(this);
mScreenHeight = ScreenUtils.getScreenHeight(this);
mScreenDensity = ScreenUtils.getScreenDensityDpi(this);
}
private MediaProjection createMediaProjection() {
Log.i(TAG, "Create MediaProjection");
return ((MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE)).getMediaProjection(mResultCode, mResultData);
}
private MediaRecorder createMediaRecorder() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
Date curDate = new Date(System.currentTimeMillis());
String curTime = formatter.format(curDate).replace(" ", "");
String videoQuality = "HD";
if (isVideoSd) videoQuality = "SD";
Log.i(TAG, "Create MediaRecorder");
MediaRecorder mediaRecorder = new MediaRecorder();
// if(isAudio) mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mediaRecorder.setOutputFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) + "/" + videoQuality + curTime + ".mp4");
mediaRecorder.setVideoSize(mScreenWidth, mScreenHeight); //after setVideoSource(), setOutFormat()
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); //after setOutputFormat()
// if(isAudio) mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); //after setOutputFormat()
int bitRate;
if (isVideoSd) {
mediaRecorder.setVideoEncodingBitRate(mScreenWidth * mScreenHeight);
mediaRecorder.setVideoFrameRate(30);
bitRate = mScreenWidth * mScreenHeight / 1000;
} else {
mediaRecorder.setVideoEncodingBitRate(5 * mScreenWidth * mScreenHeight);
mediaRecorder.setVideoFrameRate(60); //after setVideoSource(), setOutFormat()
bitRate = 5 * mScreenWidth * mScreenHeight / 1000;
}
try {
mediaRecorder.prepare();
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return mediaRecorder;
}
private VirtualDisplay createVirtualDisplay() {
Log.i(TAG, "Create VirtualDisplay");
return mMediaProjection.createVirtualDisplay(TAG, mScreenWidth, mScreenHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null, null);
}
}
点我下载,没有积分联系微信公众号“Android唐浮”
问题1: java.lang.IllegalStateException: failed to get surface
原因:目前已知在android9、10中没有Environment.DIRECTORY_MOVIES(Movies)文件夹,可以更改为Environment.DIRECTORY_DOWNLOADS(Downloads)文件夹,如果不放心,可以自己创建文件夹。
遇到问题或想持续学习,请关注公众号“Android唐浮”