MediaProjection与MediaRecorder实现录屏

来源:互联网 发布:网络电影投资公司 编辑:程序博客网 时间:2024/06/09 13:31

       纸上得来终觉浅,绝知此事要躬行,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:

@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {    surface = new Surface(surfaceTexture);}

问题

       目前上面的录制在大部分手机上是没有问题的,但是在Vivo上会偏色,偏色成粉色,我调整了所有参赛,都没有改变改情况。如果有人有解决方案希望告知。

代码

       最后附上源码Code

阅读全文
1 0
原创粉丝点击