zoukankan      html  css  js  c++  java
  • Android5.0免Root截屏,录屏

    http://blog.csdn.net/wds1181977/article/details/52174840

    MediaProjection介绍

    MediaProjection可以用来捕捉屏幕,具体来说可以截取当前屏幕和录制屏幕视频。MediaProjection由MediaProjectionManager来管理和获取。

    使用步骤

    首先获取MediaProjectionManager,和其他的Manager一样通过 Context.getSystemService() 传入参数MEDIA_PROJECTION_SERVICE获得实例。

    接着调用MediaProjectionManager.createScreenCaptureIntent()弹出dialog询问用户是否授权应用捕捉屏幕,同时覆写onActivityResult()获取授权结果。

    如果授权成功,通过MediaProjectionManager.getMediaProjection(int resultCode, Intent resultData)获取MediaProjection实例,通过MediaProjection.createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler)创建VirtualDisplay实例。实际上在上述方法中传入的surface参数,是真正用来截屏或者录屏的。

    截屏

    截屏这里用到ImageReader类,这个类的getSurface()方法获取到surface直接传入MediaProjection.createVirtualDisplay()方法中,此时就可以执行截取。通过ImageReader.acquireLatestImage()方法即可获取当前屏幕的Image,经过简单处理之后即可保存为Bitmap。

               private void startCapture() {
    
                mImageName = System.currentTimeMillis() + ".png";
    
                Log.e(TAG, "image name is : " + mImageName);
    
                Image image = mImageReader.acquireLatestImage();
    
                int width = image.getWidth();
    
                int height = image.getHeight();
    
                final Image.Plane[] planes = image.getPlanes();
    
                final ByteBuffer buffer = planes[0].getBuffer();
    
                int pixelStride = planes[0].getPixelStride();
    
                int rowStride = planes[0].getRowStride();
    
                int rowPadding = rowStride - pixelStride * width;
    
                Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
    
                bitmap.copyPixelsFromBuffer(buffer);
    
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
    
                image.close();
    
                if (bitmap != null) {
    
                    Log.e(TAG, "bitmap  create success ");
    
                    try {
    
                        File fileFolder = new File(mImagePath);
    
                        if (!fileFolder.exists())
    
                            fileFolder.mkdirs();
    
                        File file = new File(mImagePath, mImageName);
    
                        if (!file.exists()) {
    
                            Log.e(TAG, "file create success ");
    
                            file.createNewFile();
    
                        }
    
                        FileOutputStream out = new FileOutputStream(file);
    
                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
    
                        out.flush();
    
                        out.close();
    
                        Log.e(TAG, "file save success ");
    
                        Toast.makeText(this.getApplicationContext(), "截图成功", Toast.LENGTH_SHORT).show();
    
                    } catch (IOException e) {
    
                        Log.e(TAG, e.toString());
    
                        e.printStackTrace();
    
                    }
    
                }
    
            }
    

    录屏

    录屏需要用到上篇博文中的MediaCadec,这个类将原始的屏幕数据编码,在通过MediaMuxer分装为mp4格式保存。MediaCodec.createInputSurface()获取一个surface对象讲起传入MediaProjection.createVirtualDisplay()即可获取屏幕原始多媒体数据,之后读取MediaCodec编码输出数据经过MediaMuxer封装处理为mp4即可播放,实现录屏。

           private void recordVirtualDisplay() {//循环多去编解码器输出数据经过处理保存为mp4
    
            while (!mIsQuit.get()) {
    
                int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);
    
                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);
    
                    mMediaCodec.releaseOutputBuffer(index, false);
    
                }
    
            }
    
        }
    
            private void encodeToVideoTrack(int index) {//输出数据为mp4文件
    
            ByteBuffer encodedData = mMediaCodec.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) {//存在编码数据
    
                encodedData.position(mBufferInfo.offset);
    
                encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
    
                mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);//写入
    
                Log.i(TAG, "sent " + mBufferInfo.size + " bytes to muxer...");
    
            }
    
        }
    
            private void resetOutputFormat() {
    
            // should happen before receiving buffers, and should only happen once
    
            if (mMuxerStarted) {
    
                throw new IllegalStateException("output format already changed!");
    
            }
    
            MediaFormat newFormat = mMediaCodec.getOutputFormat();
    
            Log.i(TAG, "output format changed.
     new format: " + newFormat.toString());
    
            mVideoTrackIndex = mMuxer.addTrack(newFormat);
    
            mMuxer.start();
    
            mMuxerStarted = true;
    
            Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);
    
        }
    

    附录参考

    官方文档

    Android视频录制

    Android 5.0截屏

    Android录屏代码 
    本文Demo

     
     
  • 相关阅读:
    java:数组操作工具类 java.util.Arrays包 主要方法详解
    java:接口特性 接口与抽象类/普通类的区别
    mybatis教程:入门>>精通>>实战
    Java :内部类基础详解
    Java swing: 实现ActionListener监听器的三种途径
    Java:final、static关键字 详解+两者结合使用
    Java:双括号初始化 /匿名内部类初始化法
    Java:静态内部类的使用目的、使用限制、与非静态内部类的对比
    域名解析>>"记录类型" 说明
    Java:接口继承接口 (多继承机制)
  • 原文地址:https://www.cnblogs.com/jukan/p/7505219.html
Copyright © 2011-2022 走看看