服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - 浅析Android录屏 MediaRecorder

浅析Android录屏 MediaRecorder

2022-12-01 14:34峥嵘life Android

MediaRecorder类是Android sdk提供的一个专门用于音视频录制,一般利用手机麦克风采集音频,摄像头采集图片信息。这篇文章主要介绍了Android录屏 MediaRecorder介绍,需要的朋友可以参考下

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 1280x720 --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是类似的,只是流程第二部有点不同,这里不做介绍。

详情可以参考:http://www.tuohang.net/article/183324.htm

四、MediaRecorder项目示例的主要代码

这里只做了录制和停止录制,没有做相关适配,比如横竖屏切换后尺寸变化。

简单效果:

浅析Android录屏 MediaRecorder

生成的MP4文件会在sdcard目录下,并且以录屏时间为文件名。

1、MainActivity

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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和项目源码下载:

RecordMedia.rar

这个项目只是简单录屏,如果要做得好,
最好是正在通知栏/悬浮框里面控制停止,
并且可以按退出键回到主界面,
要用到服务来控制录屏屏幕的开始和停止。

共勉:时间总会过去,多做一下有意义的事情。

总结

到此这篇关于Android录屏 MediaRecorder介绍的文章就介绍到这了,更多相关android录屏 mediaRecorder内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/wenzhi20102321/article/details/105024011

延伸 · 阅读

精彩推荐