码迷,mamicode.com
首页 > 移动开发 > 详细

Android5.0以上实现对手机屏幕录制并将视频实时保存到本地(亦可实时传输)

时间:2018-09-19 16:18:29      阅读:242      评论:0      收藏:0      [点我收藏+]

标签:cli   调用   ati   NPU   levels   version   帧率   tin   mime   

前言:

经过很长的学习很研究,在实现了支持安卓4.3以上可实现屏幕录制并保存或实时发送后,根据手机系统的版本号,在android5.0以上利用原生的接口效率更高更省CPU,内存。

5.0以上编码传输可达到每90帧/s,且在手机画面不刷新时不传输数据,更省内存电量,因此在5.0以上优先使用原生接口更为合适。

设计逻辑:

初始化

1.MediaProjectionManager,

2.Intent启动mMediaProjectionManager.createScreenCaptureIntent();,

3.启动成功后开启线程

 1)创建H264编码器MediaCodec,

 2)启动VirtualDisplay,将MediaCodec创建的inputsurface通过创建mVirtualDisplay = mediaProjection.createVirtualDisplay,传入,使编码器开始不断记录并开始解码。

基础代码如下:

/**
 * 5.0屏幕录制
 */
@SuppressWarnings("ALL")
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ScreenAboveLollRecordManager {
    private static final int REQUEST_CODE = 12580;
    private MediaProjectionManager mMediaProjectionManager;
    private ScreenRecorderThread mScreenRecorderThread;
    private int mScreenWidth;
    private int mScreenHeight;
    private MediaProjection mMediaProjection;
    private MediaCodec mMediaCodec;
    private final MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
    private VirtualDisplay mVirtualDisplay;
    private RandomAccessFile mH264DataFile = null;


    public ScreenRecorderManager(Activity activity, int width, int height) {
        if (activity != null) {
            mScreenWidth = width;
            mScreenHeight = height;
            mMediaProjectionManager = (MediaProjectionManager) activity
                    .getSystemService(Context.MEDIA_PROJECTION_SERVICE);
            File file = new File(Environment.getExternalStorageDirectory().getPath()
                    + "/videoTest.264");
            try {
                mH264DataFile = new RandomAccessFile(file, "rw");
            } catch (FileNotFoundException e) {
            }
        }
    }

    //退出线程,停止录制
    public void destroy() {
        mIsRun = false;
        mMediaProjectionManager = null;
    }

//开始屏幕录制

    public void startScreenRecorde(Activity activity) {
        if (mMediaProjectionManager != null) {
            if (activity != null) {
                Intent intent = mMediaProjectionManager.createScreenCaptureIntent();
                activity.startActivityForResult(intent, REQUEST_CODE);
            }
        }
    }


    /*
     * 在Acitvity的onActivityResult调用
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode != REQUEST_CODE) {
            return;
        }

        if (mScreenRecorderThread != null || mIsRun) {
// 5.0的编码已经开启了
// activity被销毁了
            return;
        }


        if (resultCode == Activity.RESULT_OK) {
            mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
            if (mMediaProjection == null) {
// TODO 通知启动失败
                return;
            }
            mIsRun = true;
            mScreenRecorderThread = new ScreenRecorderThread();
            mScreenRecorderThread.start();
// TODO 通知开始屏幕录制
        } else {
// TODO 通知启动失败
        }
    }

    //录制视频线程
    private class ScreenRecorderThread extends Thread {
        @Override
        public void run() {
            super.run();
            createMediaCodec(mMediaProjection);
            recordVirtualDisplay();
            release();
        }
    }

    //创建编码器
    private void createMediaCodec(MediaProjection mediaProjection) {
        if (mediaProjection != null) {
            MediaCodecInfo codecInfo = selectCodec();
            if (codecInfo == null) {
                return;
            }


            MediaCodecInfo.CodecCapabilities capabilities = codecInfo
                    .getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC);
            MediaCodecInfo.CodecProfileLevel[] profileLevels = capabilities.profileLevels;


            MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC,
                    mScreenWidth, mScreenHeight);
// 设置颜色格式
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                    MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
// TODO 可根据不同分辨率设置比特率
            format.setInteger(MediaFormat.KEY_BIT_RATE, 4000000);
// 设置帧率
            format.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
// 设置关键帧间隔时间,单位s
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);

            format.setInteger(MediaFormat.KEY_PROFILE,
                    MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
            int maxCodecProfileLevel = MediaCodecInfo.CodecProfileLevel.AVCLevel1;
            for (MediaCodecInfo.CodecProfileLevel codecProfileLevel : profileLevels) {
                if (codecProfileLevel.profile != MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline) {
                    continue;
                }
                if (codecProfileLevel.level > maxCodecProfileLevel) {
                    maxCodecProfileLevel = codecProfileLevel.level;
                }
            }
            format.setInteger("level", maxCodecProfileLevel);
            try {
// 实例化一个支持给定MIME类型的数据输出的编码器
                mMediaCodec = MediaCodec.createByCodecName(codecInfo.getName());
// 配置好格式参数
                mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
// 请求一个surface用于编码器的输入
                Surface encoderSurface = mMediaCodec.createInputSurface();
                mVirtualDisplay = mediaProjection.createVirtualDisplay("display", mScreenWidth,
                        mScreenHeight, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                        encoderSurface, null, null);
                mMediaCodec.start();
            } catch (Exception e) {
            }
        }
    }

    //选择编码器级别
    @SuppressWarnings("deprecation")
    private static MediaCodecInfo selectCodec() {
        int numCodecs = MediaCodecList.getCodecCount();
        for (int i = 0; i < numCodecs; i++) {
            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
            if (!codecInfo.isEncoder()) {
                continue;
            }
            String[] types = codecInfo.getSupportedTypes();
            for (String type : types) {
                if (MediaFormat.MIMETYPE_VIDEO_AVC.equalsIgnoreCase(type)) {
                    return codecInfo;
                }
            }
        }
        return null;
    }


    private boolean mIsRun = false;

    //录制虚拟视频
    @SuppressWarnings("deprecation")
    private void recordVirtualDisplay() {
        while (mIsRun) {
            try {
                if (mMediaCodec == null) {
                    return;
                }
                ByteBuffer[] encoderOutputBuffers = mMediaCodec.getOutputBuffers();
                while (mIsRun && mIsOnStart) {
                    int encoderStatus = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 0);
                    if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        break;
                    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        encoderOutputBuffers = mMediaCodec.getOutputBuffers();
                    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    } else if (encoderStatus < 0) {
                   // let‘s ignore it
                    } else {
                        if (mBufferInfo.size != 0) {
                            byte[] dataToWrite = new byte[mBufferInfo.size];
                            ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
                            encodedData.position(mBufferInfo.offset);
                            encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
                            encodedData.get(dataToWrite, 0, mBufferInfo.size);
                            saveH264DataToFile(dataToWrite);
                        }
                        mMediaCodec.releaseOutputBuffer(encoderStatus, false);
                    }
                }
            } catch (Exception e) {
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
            }
        } else{
          // 非横屏睡眠500毫秒等待
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    } else

    {
// app不在前台,睡眠500毫秒,较少cpu的消耗
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }

    //保存H264视频到本地
    private void saveH264DataToFile(byte[] dataToWrite) {
        try {
            mH264DataFile.write(dataToWrite, 0, dataToWrite.length);
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        }
    }

    //释放所有资源
    private void release() {
        if (mMediaCodec != null) {
            mMediaCodec.stop();
            mMediaCodec.release();
            mMediaCodec = null;
        }
        if (mVirtualDisplay != null) {
            mVirtualDisplay.release();
            mVirtualDisplay = null;
        }
        if (mMediaProjection != null) {
            mMediaProjection.stop();
            mMediaProjection = null;
        }
        mScreenRecorderThread = null;
    }
}

 

Android5.0以上实现对手机屏幕录制并将视频实时保存到本地(亦可实时传输)

标签:cli   调用   ati   NPU   levels   version   帧率   tin   mime   

原文地址:https://www.cnblogs.com/zhujiabin/p/9674514.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!