发布于2022年11月4日3年前 VirtualDisplay 一、介绍 代表一个虚拟显示器。 虚拟显示器的内容被渲染到您必须提供给createVirtualDisplay()的Surface 。 二、使用 1、createVirtualDisplay 通常我们使用DisplayManager.createVirtualDisplay()来创建虚拟显示。 一下是创建虚拟显示的具体方法: createVirtualDisplay(@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags); createVirtualDisplay(@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) 参数: name – 虚拟显示器的名称,必须非空。 width – 虚拟显示的宽度(以像素为单位),必须大于 0。 height – 虚拟显示器的高度(以像素为单位),必须大于 0。 densityDpi – 以 dpi 为单位的虚拟显示密度,必须大于 0。 surface – 虚拟显示器的内容应该被渲染到的表面,如果最初没有,则为 null。 flags – 虚拟显示标志的组合: VIRTUAL_DISPLAY_FLAG_PUBLIC 、 VIRTUAL_DISPLAY_FLAG_PRESENTATION 、 VIRTUAL_DISPLAY_FLAG_SECURE 、 VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY或VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR 。 callback - 当VirtualDisplay的状态改变时调用的回调 handler – 应在其上调用侦听器的处理程序,如果应在调用线程的 Looper 上调用侦听器,则为 null。 虚拟显示的内容被渲染到应用程序提供的Surface 。虚拟显示的行为取决于提供给此方法的标志。 默认情况下,虚拟显示被创建为私有的、非展示的和不安全的。 使用某些标志可能需要权限。 2、分析参数flags 1.VIRTUAL_DISPLAY_FLAG_PUBLIC 虚拟显示标志:创建公共显示。公共虚拟显示器 设置此标志时,虚拟显示是公开的。公共虚拟显示器的行为与大多数连接到系统的其他显示器(例如外部或无线显示器)的行为类似。 应用程序可以在显示器上打开窗口,系统可以将其他显示器的内容镜像到它上面。 2.VIRTUAL_DISPLAY_FLAG_PRESENTATION 虚拟显示标志:创建演示显示。演示虚拟显示 当该标志被设置时,虚拟显示器被注册为presentation display category中的presentation display category 。 应用程序可以自动将其内容投影到演示显示以提供更丰富的第二屏幕体验。 3.VIRTUAL_DISPLAY_FLAG_SECURE 虚拟显示标志:创建安全显示。安全的虚拟显示器 设置此标志后,虚拟显示被视为安全。 来电者承诺采取合理措施,例如无线加密,以防止显示内容被截取或记录在持久性介质上。另外:创建安全的虚拟显示器需要 CAPTURE_SECURE_VIDEO_OUTPUT 权限。 此权限保留供系统组件使用,不适用于第三方应用程序 4.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY 虚拟显示标志:只显示该显示器本身的内容; 不要镜像另一个显示器的内容。 此标志与VIRTUAL_DISPLAY_FLAG_PUBLIC结合使用。 通常,如果公共虚拟显示器没有自己的窗口,它们将自动镜像默认显示器的内容。 当这个标志被指定时,虚拟显示器将只显示它自己的内容,如果它没有窗口,它将被消隐。 5.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR 虚拟显示标志:当没有显示内容时,允许将内容镜像到私人显示器上。此标志仅用于在创建私有显示时覆盖默认行为。 另外:创建自动镜像虚拟显示器需要 CAPTURE_VIDEO_OUTPUT 或 CAPTURE_SECURE_VIDEO_OUTPUT 权限。 这些权限保留供系统组件使用,第三方应用程序无法使用。 或者,可以使用适当的MediaProjection来创建自动镜像虚拟显示。 3、MediaProjection 授予应用程序捕获屏幕内容和/或录制系统音频的能力的令牌。 授予的确切功能取决于 MediaProjection 的类型。 屏幕捕获会话可以通过MediaProjectionManager.createScreenCaptureIntent启动。 这允许捕获屏幕内容,但不能捕获系统音频。 示例: MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); if (mProjectionManager != null) { startActivityForResult(mProjectionManager.createScreenCaptureIntent(), 1); } 在onActivityResult回调中处理 @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); String path=GetRealPath.getPath(this,uri); if(requestCode==1){ if(resultCode==RESULT_OK){ MediaProjection mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); VirtualDisplay virtualDisplay =mMediaProjection.createVirtualDisplay("screen_shut", mWidth, mHeight, mDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mSurface,null,null); } } } createVirtualDisplay参数: name: 是生成的VirtualDisplay实例的名称; width, height: 分别是生成实例的宽高; dpi: 生成实例的像素密度,必须大于0,一般都取1; surface: 这个比较重要,是你生成的VirtualDisplay的载体,我的理解是,VirtualDisplay的内容是一帧帧的屏幕截图(所以你看到是有宽高,像素密度等设置),所以MediaProjection获取到的其实是一帧帧的图,然后通过surface来顺序播放这些图片,形成视频。 MediaProjection最好在前台服务中创建并在清单文件中设置Service为foregroundServiceType="mediaProjection",以上仅仅为示例 三、使用ImageReader获取虚拟显示器中的图像 1、ImageReader ImageReader 类允许应用程序直接访问渲染到Surface图像数据;图像数据封装在Image对象中,可以同时访问多个这样的对象,最多可达maxImages构造函数参数指定的数量。 通过其Surface发送到 ImageReader 的新图像将排队,直到通过acquireLatestImage或acquireNextImage调用访问。 由于内存限制,如果 ImageReader 没有以等于生产速率的速率获取和释放图像,则图像源最终将在尝试渲染到 Surface 时停止或丢弃图像。 2、实例化newInstance 静态方法直接调用 ImageReader.newInstance( @IntRange(from = 1) int width, @IntRange(from = 1) int height, @Format int format, @IntRange(from = 1) int maxImages); width – 此阅读器将生成的图像的默认宽度(以像素为单位)。 height – 此阅读器将生成的图像的默认高度(以像素为单位)。 格式 - 此阅读器将生成的图像格式。 这必须是ImageFormat或android.graphics.PixelFormat常量之一。 请注意,并非所有格式都受支持,例如 ImageFormat.NV21。 maxImages – 用户希望同时访问的最大图像数。 这应该尽可能小以限制内存使用。 一旦用户获得了 maxImages 图像,必须先释放其中一个图像,然后才能通过。 3、获取VirtualDisplay中的图像 创建virtualdisplay时传入imageReader.getSurface() virtualDisplay=mDisplayManager.createVirtualDisplay("getImage", mWidth, mHeight, mDensity, mImageReader.getSurface(), DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION); imageReader.getSurface():获取可用于为此ImageReader生成Images的Surface 。 注册一个侦听器,以便在 ImageReader 中有新图像可用时调用。 注意这里的新图像,也就是说如果你的虚拟显示中始终没有新的操作或者说界面没有变化,ImageReader是监听不到新的图像的,log可能不会打印,但不意味着虚拟显示中的图像消失了。 //一般而言,如果你的Handler是要来刷新操作UI的,那么就需要在主线程下跑。 Handler mHandler=new Handler(Looper.getMainLooper()); mImageReader.setOnImageAvailableListener(new ImageAvailableListener(),mHandler); private class ImageAvailableListener implements ImageReader.OnImageAvailableListener { @Override public void onImageAvailable(ImageReader reader) { if (reader != mImageReader) { return; } //SystemClock.sleep(2000); Image image = reader.acquireNextImage(); if (image != null) { 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.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); //保存图片到本地 ImageSaveUtil.saveBitmap2file(bitmap,activity,String.valueOf(System.currentTimeMillis())); Log.d(TAG, "New image available from virtual display." + i); image.close(); i++; } } }
创建帐户或登录后发表意见