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

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

服务器之家 - 编程语言 - Android - Android录屏的三种解决方案

Android录屏的三种解决方案

2022-12-01 14:36charles0427 Android

这篇文章主要介绍了Android录屏的三种方案,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

本文总结三种用于安卓录屏的解决方案:

adb shell命令screenrecord
MediaRecorder, MediaProjection
MediaProjection , MediaCodec和MediaMuxer

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,命令不会被执行;

MediaRecorder

MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。

录屏过程可以分成两个部分,即通过MediaProjectionManage申请录屏权限,用户允许后开始录制屏幕;然后通过MediaRecorder对音视频数据进行处理。

获取MediaProjectionManager实例

MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService("media_projection");

申请权限

Intent captureIntent = mProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, LOCAL_REQUEST_CODE);

createScreenCaptureIntent()这个方法会返回一个intent,你可以通过startActivityForResult方法来传递这个intent,为了能开始屏幕捕捉,activity会提示用户是否允许屏幕捕捉(为了防止开发者做一个木马,来捕获用户私人信息),你可以通过getMediaProjection来获取屏幕捕捉的结果。

在onActivityResult中获取结果

?
1
2
3
4
5
6
7
8
9
10
11
12
@Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    MediaProjection mediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
    if (mediaProjection == null) {
    Log.e(TAG, "media projection is null");
    return;
  }
    File file = new File("xx.mp4"); //录屏生成文件
    mediaRecord = new MediaRecordService(displayWidth, displayHeight, 6000000, 1,
      mediaProjection, file.getAbsolutePath());
    mediaRecord.start();
}

创建MediaRecorder进程

?
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
package com.unionpay.service;
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");
  }
}

MediaCodec与MediaMuxer

MediaCodec提供对音视频压缩编码和解码功能,MediaMuxer可以将音视频混合生成多媒体文件,生成MP4文件。
与MediaRecorder类似,都需要先通过MediaProjectionManager获取录屏权限,在回调中进行屏幕数据处理。

这里创建另一个进程:

?
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.unionpay.service;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.projection.MediaProjection;
import android.util.Log;
import android.view.Surface;
public class ScreenRecordService extends Thread{
    private static final String TAG = "ScreenRecordService";
  private int mWidth;
  private int mHeight;
  private int mBitRate;
  private int mDpi;
  private String mDstPath;
  private MediaProjection mMediaProjection;
  // parameters for the encoder
  private static final String MIME_TYPE = "video/avc"; // H.264 Advanced
                 // Video Coding
  private static final int FRAME_RATE = 30; // 30 fps
  private static final int IFRAME_INTERVAL = 10; // 10 seconds between
                // I-frames
  private static final int TIMEOUT_US = 10000;
  private MediaCodec mEncoder;
  private Surface mSurface;
  private MediaMuxer mMuxer;
  private boolean mMuxerStarted = false;
  private int mVideoTrackIndex = -1;
  private AtomicBoolean mQuit = new AtomicBoolean(false);
  private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
  private VirtualDisplay mVirtualDisplay;
  public ScreenRecordService(int width, int height, int bitrate, int dpi, MediaProjection mp, String dstPath) {
    super(TAG);
    mWidth = width;
    mHeight = height;
    mBitRate = bitrate;
    mDpi = dpi;
    mMediaProjection = mp;
    mDstPath = dstPath;
  }
  /**
   * stop task
   */
  public final void quit() {
    mQuit.set(true);
  }
  @Override
  public void run() {
    try {
    try {
      prepareEncoder();
      mMuxer = new MediaMuxer(mDstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG + "-display", mWidth, mHeight, mDpi,
      DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, null);
    Log.d(TAG, "created virtual display: " + mVirtualDisplay);
    recordVirtualDisplay();
    } finally {
    release();
    }
  }
  private void recordVirtualDisplay() {
    while (!mQuit.get()) {
    int index = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
//   Log.i(TAG, "dequeue output buffer index=" + index);
    if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
      // 后续输出格式变化
      resetOutputFormat();
    } else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
      // 请求超时
//     Log.d(TAG, "retrieving buffers time out!");
      try {
      // wait 10ms
      Thread.sleep(10);
      } catch (InterruptedException e) {
      }
    } else if (index >= 0) {
      // 有效输出
      if (!mMuxerStarted) {
      throw new IllegalStateException("MediaMuxer dose not call addTrack(format) ");
      }
      encodeToVideoTrack(index);
      mEncoder.releaseOutputBuffer(index, false);
    }
    }
  }
  /**
   * 硬解码获取实时帧数据并写入mp4文件
   *
   * @param index
   */
  private void encodeToVideoTrack(int index) {
    // 获取到的实时帧视频数据
    ByteBuffer encodedData = mEncoder.getOutputBuffer(index);
    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
    // The codec config data was pulled out and fed to the muxer
    // when we got
    // the INFO_OUTPUT_FORMAT_CHANGED status.
    // Ignore it.
    Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
    mBufferInfo.size = 0;
    }
    if (mBufferInfo.size == 0) {
    Log.d(TAG, "info.size == 0, drop it.");
    encodedData = null;
    } else {
//   Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size + ", presentationTimeUs="
//     + mBufferInfo.presentationTimeUs + ", offset=" + mBufferInfo.offset);
    }
    if (encodedData != null) {
    mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
    }
  }
  private void resetOutputFormat() {
    // should happen before receiving buffers, and should only happen
    // once
    if (mMuxerStarted) {
    throw new IllegalStateException("output format already changed!");
    }
    MediaFormat newFormat = mEncoder.getOutputFormat();
    mVideoTrackIndex = mMuxer.addTrack(newFormat);
    mMuxer.start();
    mMuxerStarted = true;
    Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);
  }
  private void prepareEncoder() throws IOException {
    MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    Log.d(TAG, "created video format: " + format);
    mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
    mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mSurface = mEncoder.createInputSurface();
    Log.d(TAG, "created input surface: " + mSurface);
    mEncoder.start();
  }
  private void release() {
    if (mEncoder != null) {
    mEncoder.stop();
    mEncoder.release();
    mEncoder = null;
    }
    if (mVirtualDisplay != null) {
    mVirtualDisplay.release();
    }
    if (mMediaProjection != null) {
    mMediaProjection.stop();
    }
    if (mMuxer != null) {
    mMuxer.stop();
    mMuxer.release();
    mMuxer = null;
    }
  }
}

该进程只实现了视频录制,调用该进程只需修改主进程中的onActivityResult方法。

总结

MediaProjection似乎只有在屏幕发生变化时才传输,因此录屏推流的画面显得不够流畅

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

原文链接:https://www.jianshu.com/p/8b313692ac85

延伸 · 阅读

精彩推荐
  • AndroidAndroid PopupWindow实现遮罩层效果

    Android PopupWindow实现遮罩层效果

    这篇文章主要为大家详细介绍了Android PopupWindow实现遮罩层效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    carter_yu4882022-08-14
  • AndroidAndroid显示全文折叠控件使用方法详解

    Android显示全文折叠控件使用方法详解

    这篇文章主要为大家详细介绍了Android显示全文折叠控件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    carbs_wang10202022-08-28
  • Androidandroid当前apn的状态以及获取方法

    android当前apn的状态以及获取方法

    在绝大多数android机器etc路径下存放一个的apns-conf.xml文件,表示当前机器使用的apn信息通过root机器可以push出来看看,具体路径可以上网搜下,接下来介绍获...

    Android开发网10612021-01-01
  • AndroidAndroid SDK 百度地图通过poi城市内检索简介接口的使用

    Android SDK 百度地图通过poi城市内检索简介接口的使用

    这篇文章主要介绍了Android SDK 百度地图通过poi城市内检索简介接口的使用的相关资料,需要的朋友可以参考下...

    凌晨零点零零9112021-05-26
  • Androidandroid分享纯图片到QQ空间实现方式

    android分享纯图片到QQ空间实现方式

    今天小编就为大家分享一篇关于android分享纯图片到QQ空间实现方式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随...

    weixin_412391273972022-10-12
  • AndroidAndroid支付宝支付设计开发

    Android支付宝支付设计开发

    这篇文章主要为大家详细介绍了Android支付宝支付设计开发,设计支付宝支付开发方案,感兴趣的小伙伴们可以参考一下...

    Android开发网12142021-06-09
  • AndroidAndroid启动屏实现左右滑动切换查看功能

    Android启动屏实现左右滑动切换查看功能

    这篇文章主要介绍了Android启动屏实现左右滑动切换查看功能的相关资料,针对新功能属性介绍和启动屏进行详细讲解,感兴趣的小伙伴们可以参考一下...

    IT_xiao小巫4732021-05-20
  • AndroidAndroid串口操作方法实例

    Android串口操作方法实例

    这篇文章主要介绍了Android串口操作方法实例,本文共分5个步骤讲解了Android串口操作方法,并给出代码实例,需要的朋友可以参考下...

    Android开发网10702021-03-18