MediaRecorder類介紹:
MediaRecorder類是Android sdk提供的一個專門用于音視訊錄制,一般利用手機麥克風采集音頻,攝像頭采集圖檔資訊。
Android錄屏的三種方案
1、adb shell指令screenrecord
2、MediaRecorder, MediaProjection
3、MediaCodec和MediaMuxer, MediaProjection ,
一、screenrecord指令
screenrecord是一個shell指令,支援Android4.4(API level 19)以上,
錄制的視訊格式為mp4 ,存放到手機sd卡裡,預設錄制時間為180s
adb shell screenrecord –size 1280×720 –bit-rate 6000000 –time-limit 30 /sdcard/demo.mp4
–size 指定視訊分辨率,根據手機情況決定
–bit-rate 指定視訊比特率,預設為4M,該值越小,儲存的視訊檔案越小;
–time-limit 指定錄制時長,若設定大于180,指令不會被執行;
并不是是以手機都執行screenrecord指令,部分手機不識别。
我在幾款華為手機就沒執行成功報錯:
/system/bin/sh: screenrecord: inaccessible or not found
二、 MediaRecorder
MediaProjection是Android5.0後才開放的螢幕采集接口,通過系統級服務MediaProjectionManager進行管理。
這裡先整體說一下螢幕錄制的流程,不然看起來費勁。
1、通過startActivityForResult(Intent intent)判斷是否錄屏授權的Activity
其中intent對象就需要MediaProjectionManager.createScreenCaptureIntent();擷取
2、在onActivityResult回調方法中做具體錄屏工作
比如:建立MediaRecorder,設定MP4檔案路徑
建立VirtualDisplay,設定螢幕相關參數
如果不在onActivityResult回調中執行會有問題。
3、開始錄屏
MediaRecorder.start()
4、停止錄屏
MediaRecorder.reset();
MediaRecorder.release();
錄屏過程用到錄音權限和資料讀寫權限。
三、MediaCodec和MediaMuxer
MediaCodec提供對音視訊壓縮編碼和解碼功能,MediaMuxer可以将音視訊混合生成多媒體檔案,生成MP4檔案。
這個錄屏的方式和MediaRecorder是類似的,隻是流程第二部有點不同,這裡不做介紹。
詳情可以參考:https://www.zalou.cn/article/183324.htm
四、MediaRecorder項目示例的主要代碼
這裡隻做了錄制和停止錄制,沒有做相關适配,比如橫豎屏切換後尺寸變化。
簡單效果:

生成的MP4檔案會在sdcard目錄下,并且以錄屏時間為檔案名。
1、MainActivity
package com.liwenzhi.screen;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String TAG = "MainActivity";
MediaProjectionManager mProjectionManager;
MediaRecordService mediaRecord;
int displayWidth = 1280;
int displayHeight = 1920;
Button btn_start_record;
Button btn_stop_record;
TextView tv_time;
int REQUEST_CODE_PERMISSIONS = 99;
int REQUEST_CODE_SCREEN = 100;
//錄音權限和資料讀寫權限
String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
boolean mPassPermissions = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initEvent();
}
private void initView() {
btn_start_record = findViewById(R.id.btn_start_record);
btn_stop_record = findViewById(R.id.btn_stop_record);
tv_time = findViewById(R.id.tv_time);
}
private void initData() {
//權限申請
//逐個判斷你要的權限是否已經通過
judgePermissions();
if (!mPassPermissions) {
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_PERMISSIONS);
}
}
private void initEvent() {
btn_start_record.setOnClickListener(this);
btn_stop_record.setOnClickListener(this);
}
// 申請權限後的回調
// 1、錄音和讀寫
// 2、錄屏startActivityForResult後的回調
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//錄音和讀寫權限
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (resultCode != Activity.RESULT_OK) {
mPassPermissions = false;
}
} else
// 錄屏權限
if (requestCode == REQUEST_CODE_SCREEN) {
if (resultCode == Activity.RESULT_OK) {
try {
// mediaProjection 如果不在權限申請中回調,擷取到的對象為空
MediaProjection mediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
if (mediaProjection == null) {
Log.e(TAG, "media projection is null");
return;
}
File file = new File("/sdcard/" + MyTimeUtils.getTimeString("yyyy-MM-dd_HH:mm:ss") + ".mp4"); //錄屏生成檔案
if (!file.exists()) {
file.createNewFile();
}
mediaRecord = new MediaRecordService(displayWidth, displayHeight, 6000000, 1,
mediaProjection, file.getAbsolutePath());
mediaRecord.start();
second = 0;
mHandler.sendEmptyMessageDelayed(10, 1000);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Toast.makeText(MainActivity.this, "錄屏失敗", Toast.LENGTH_LONG).show();
}
}
}
@Override
public void onBackPressed() {
//super.onBackPressed();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_record:
startRecord();
break;
case R.id.btn_stop_record:
stopRecord();
break;
}
}
// 開始錄屏
private void startRecord() {
judgePermissions();
if (!mPassPermissions) {
Toast.makeText(this, "基礎權限沒通過!", Toast.LENGTH_LONG).show();
return;
}
mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (mProjectionManager != null) {
Intent intent = mProjectionManager.createScreenCaptureIntent();
PackageManager packageManager = getPackageManager();
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
//存在錄屏授權的Activity
startActivityForResult(intent, REQUEST_CODE_SCREEN);
} else {
Toast.makeText(this, "沒有錄屏權限!", Toast.LENGTH_LONG).show();
}
}
}
// 停止錄屏
private void stopRecord() {
mediaRecord.release();
mHandler.removeMessages(10);
}
int second;
// 計算時間
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 10:
second++;
tv_time.setText(second + "");
mHandler.sendEmptyMessageDelayed(10, 1000);
break;
}
}
};
//判斷每個權限是否通過
private void judgePermissions() {
boolean permission = true;
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
// 未授予的權限
permission = false;
}
}
mPassPermissions = permission;
}
}
複制
2、MediaRecordService
package com.liwenzhi.screen;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.util.Log;
public class MediaRecordService extends Thread {
private static final String TAG = "MediaRecordService";
private int mWidth;
private int mHeight;
private int mBitRate;
private int mDpi;
private String mDstPath;
private MediaRecorder mMediaRecorder;
private MediaProjection mMediaProjection;
private static final int FRAME_RATE = 60; // 60 fps
private VirtualDisplay mVirtualDisplay;
public MediaRecordService(int width, int height, int bitrate, int dpi, MediaProjection mp, String dstPath) {
mWidth = width;
mHeight = height;
mBitRate = bitrate;
mDpi = dpi;
mMediaProjection = mp;
mDstPath = dstPath;
}
@Override
public void run() {
try {
initMediaRecorder();
//在mediarecorder.prepare()方法後調用
mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG + "-display", mWidth, mHeight, mDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mMediaRecorder.getSurface(), null, null);
Log.i(TAG, "created virtual display: " + mVirtualDisplay);
mMediaRecorder.start();
Log.i(TAG, "mediarecorder start");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 初始化MediaRecorder
*
* @return
*/
public void initMediaRecorder() {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(mDstPath);
mMediaRecorder.setVideoSize(mWidth, mHeight);
mMediaRecorder.setVideoFrameRate(FRAME_RATE);
mMediaRecorder.setVideoEncodingBitRate(mBitRate);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
try {
mMediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
Log.i(TAG, "media recorder" + mBitRate + "kps");
}
public void release() {
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
if (mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
mMediaProjection.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
}
if (mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
Log.i(TAG, "release");
}
}
複制
3、MyTimeUtils
package com.liwenzhi.screen;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* a Utils for time
* <p
* identify the following letter: :"yyyy-MM-dd DD HH:mm:ss SSS"
* * method1: long getTimeLong()
* * method2: int getTimeInt(String filter)
* * method3: String getTimeString()
* * method4: String getTimeString(long time)
* * method5: String getTimeString(long time, String filter)
* * method6: String getTimeString( String filter)
*/
public class MyTimeUtils {
public static long getTimeLong() {
return System.currentTimeMillis();
}
public static int getTimeInt(String filter) {
SimpleDateFormat format = new SimpleDateFormat(filter);
String time = format.format(new Date());
return Integer.parseInt(time);
}
public static final String getTimeString() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(new Date(getTimeLong()));
}
public static final String getTimeString(long time) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return format.format(new Date(time));
}
public static final String getTimeString(long time, String filter) {
SimpleDateFormat format = new SimpleDateFormat(filter);
return format.format(new Date(time));
}
public static final String getTimeString(String filter) {
SimpleDateFormat format = new SimpleDateFormat(filter);
return format.format(new Date(getTimeLong()));
}
public static Long getTimeLong(String filter, String date) {
try {
SimpleDateFormat format = new SimpleDateFormat(filter);
Date dateTime = format.parse(date);
return dateTime.getTime();
} catch (Exception e) {
e.printStackTrace();
}
return 0L;
}
}
複制
測試apk和項目源碼下載下傳:
http://xiazai.zalou.cn/202003/yuanma/RecordMedia_jb51.rar
這個項目隻是簡單錄屏,如果要做得好,
最好是正在通知欄/懸浮框裡面控制停止,
并且可以按退出鍵回到主界面,
要用到服務來控制錄屏螢幕的開始和停止。
共勉:時間總會過去,多做一下有意義的事情。
總結
到此這篇關于Android錄屏 MediaRecorder介紹的文章就介紹到這了,更多相關android錄屏 mediaRecorder内容請搜尋ZaLou.Cn以前的文章或繼續浏覽下面的相關文章希望大家以後多多支援ZaLou.Cn!