Android5.0以上使用MediaProjection截图和录屏
发布日期:2021-10-04 02:53:49 浏览次数:7 分类:技术文章

本文共 5590 字,大约阅读时间需要 18 分钟。

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.\n new format: " + newFormat.toString());        mVideoTrackIndex = mMuxer.addTrack(newFormat);        mMuxer.start();        mMuxerStarted = true;        Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);    }

附录参考

转载地址:https://blog.csdn.net/leif_/article/details/50971659 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android中的Context理解
下一篇:Android MediaCodec使用介绍

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月19日 20时28分00秒