MediaProjection与MediaRecorder实现录屏
Posted freeCodeSunny
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MediaProjection与MediaRecorder实现录屏相关的知识,希望对你有一定的参考价值。
纸上得来终觉浅,绝知此事要躬行,android在5.0提供了MediaProjection来实现录屏,但是一直都没有尝试过,这里尝试了一下该方式进行录屏。
其实Demo已经写好很久了,但是始终有一个问题,在某些机型上会偏色,因此这里写出来看看是否有人遇到同样的问题,且希望告知解决方案。
录制
这里界面上有两个按钮,一个控制录制与暂停,另外一个是播放按钮,既然是录制,当然录制完成我们需要看看录制的效果。
布局界面如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.example.activity.ScreenRecordActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start"/>
<Button
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="play"/>
</LinearLayout>
<Chronometer
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
<TextureView
android:id="@+id/texture"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
两个按钮,一个播放一个停止
Chronometer在界面上改变数字,便于区分视频是否在在录制
TextureView播放界面
回到Activity,我们这里先初始化一下默认信息
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void init()
metrics = this.getResources().getDisplayMetrics();
width = 720;
height = 1280;
//printInfo();
manager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
这里宽高就默认为720*1280,也就是视频尺寸
获取了MediaProjectionManager,后面需要他来获得MediaProjection
上面的准备工作就做完了,这里我们将处理逻辑都放到点击事件上,这里我们点击recoder按钮:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void onRecord()
if (isRecording)
stopRecord();
else
startRecord();
isRecording = !isRecording;
record.setText(isRecording ? "stop" : "start");
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void startRecord()
if (mediaProjection != null) // 说明已经请求过权限了
onStartRecord();
else
Intent captureIntent = manager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE);
private void stopRecord()
if (recorder != null)
recorder.stop();
recorder.reset();
点击后执行onRecord,这里做了区分如果是录制,下一次点击则暂停,第一次会执行startRecord.
这里我们来主要看看startRecord,这里我们首先判断mediaProjection是否为空,不为空开始录制,为空我们打开了一个Intent,这是因为录屏是需要权限的。打开对应的页面获得权限。那这里第一次会走到onActivityResult,在onActivityResult中我们主要获取了MediaProjection,如果为空则说明失败了,不为空会进入onStartRecord函数。
onStartRecord主要干了三件事,第一初始化MediaRecorder,第二创建VirtualDisplay
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void onStartRecord()
initMediaRecorder();
createVirtualDisplay();
recorder.start();
chronometer.start();
我们来看看初始化MediaRecorder:
private void initMediaRecorder()
if (recorder == null)
recorder = new MediaRecorder();
recorder.setAudiosource(MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
recorder.setVideoSize(width, height);
recorder.setVideoEncodingBitRate(4 * width * height);
recorder.setVideoFrameRate(30);
else
recorder.reset();
recorder.setOutputFile(getFilePath());
prepare();
private void prepare()
try
recorder.prepare();
catch (IOException e)
e.printStackTrace();
releaseRecorder();
private String getFilePath()
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHss");
String time = dateFormat.format(date);
String absolutePath = getExternalCacheDir().getAbsolutePath();
StringBuilder sb = new StringBuilder();
sb.append(absolutePath).append("/cap/").append(time).append(".3gp");
fileName = sb.toString();
File file = new File(fileName);
File parentFile = file.getParentFile();
if (!parentFile.exists())
parentFile.mkdir();
return fileName;
上面主要构造了一个MediaRecorder实例,设置了音频,视频,输出格式,编码格式,视频尺寸,码率,帧率,最后设置了视频输出路径
Note:上面设置的参数有先后顺序,一定不能乱序
最后调用了prepare,所有的参数都在prepares之前
我们来继续看看createVirtualDisplay:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void createVirtualDisplay()
virtualDisplay = mediaProjection.createVirtualDisplay("ScreenRecordActivity", width, height, metrics
.densityDpi, VIRTUAL_DISPLAY_FLAGS, recorder.getSurface(), null, null);
这里我们创建了一个VirtualDisplay,设置了宽,高,密度
设置了recorder.getSurface()将两个联系在一起
上面的最后一步就是调用了MediaRecorder的start函数,进行录制。
其他的一些函数都是清场函数,释放占用的资源
播放
上面已经进行了录制。那这里我们需要进行播放,播放我们采用MediaPlay,MediaPlay大家有用的很多了,这里就大致讲述一下:
private void initMediaPlay()
if (mediaPlayer == null)
mediaPlayer = new MediaPlayer();
else
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
try
mediaPlayer.setDataSource(fileName);
mediaPlayer.setSurface(surface);
mediaPlayer.prepare();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener()
@Override
public void onPrepared(MediaPlayer mp)
mediaPlayer.start();
);
catch (IOException e)
e.printStackTrace();
设置了视频流的路径
这里主要看看setSurface(surface)函数,这里我们设置了一个surface,这里的surface就是之前TextureView构造出来的surface,将视频流与TextureView联系起来。
那这里的surface是什么构造出来的,我们对TextureView设置了SurfaceTextureListener,在回调中构造surface:
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height)
surface = new Surface(surfaceTexture);
问题
目前上面的录制在大部分手机上是没有问题的,但是在Vivo上会偏色,偏色成粉色,我调整了所有参赛,都没有改变改情况。如果有人有解决方案希望告知。
代码
最后附上源码Code
以上是关于MediaProjection与MediaRecorder实现录屏的主要内容,如果未能解决你的问题,请参考以下文章
Android录屏功能实现——MediaProjection
API 级别 29 - MediaProjection 始终请求许可
android Q中无法识别MediaProjection服务类型