【Android】VirtualDisplay创建流程及原理

04-06 阅读 0评论

Android VirtualDisplay创建流程及原理

  • Android DisplayManager提供了createVirtualDisplay接口,用于创建虚拟屏。虚拟屏可用于录屏(网上很多资料说这个功能),分屏幕(比如一块很长的屏幕,通过虚拟屏分出不同的区域)等等。

    创建VirtualDisplay

    • DisplayManager中的函数原型如下。后两个Hide的API,只有平台的应用才可以使用
      // frameworks/base/core/java/android/hardware/display/DisplayManager.java
      public VirtualDisplay createVirtualDisplay(@NonNull String name,
      		int width, int height, int densityDpi, @Nullable Surface surface, int flags) {
      }
      public VirtualDisplay createVirtualDisplay(@NonNull String name,
      		int width, int height, int densityDpi, @Nullable Surface surface, int flags,
      		@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
      }
      /** @hide */
      public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
      		@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
      		int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
      		@Nullable String uniqueId) {
      }
      /** @hide */
      public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
      		@NonNull VirtualDisplayConfig virtualDisplayConfig,
      		@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
      }
      
      • 补充一点,MediaProjection中也提供了 createVirtualDisplay这个接口,实际上也是通过调用DisplayManager实现的功能。
        // frameworks/base/media/java/android/media/projection/MediaProjection.java
            public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig,
                    @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
                DisplayManager dm = mContext.getSystemService(DisplayManager.class);
                // 调用DisplayManager的接口
                return dm.createVirtualDisplay(this, virtualDisplayConfig, callback, handler);
            }
        
        • 创建VirtualDisplay时,需要传入Surface。**VirtualDisplay上要绘制的内容,实际是通过传入的Surface显示出来的。**比如在主屏(根据物理屏,分配逻辑Display)上创建了一个SurfaceView,通过把这个SurfaceView传给VirtualDisplay。那么VirtualDisplay的 内容,实际上是在主屏的SurfaceView上显示的。下面是一段Android原生的例子。
          // frameworks/base/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
          private Display createVirtualDisplay() {
          	final String displayName = "NavVirtualDisplay";
          	final DisplayInfo displayInfo = new DisplayInfo();
          	mContext.getDisplay().getDisplayInfo(displayInfo);
          	final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
          	// 创建ImageReader,通过它得到一张Surface
          	mReader = ImageReader.newInstance(displayInfo.logicalWidth,
          			displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
          	assertNotNull("ImageReader must not be null", mReader);
          	// 创建虚拟屏,传入Surface。
          	mVirtualDisplay = displayManager.createVirtualDisplay(displayName, displayInfo.logicalWidth,
          			displayInfo.logicalHeight, displayInfo.logicalDensityDpi, mReader.getSurface(),
          			0 /*flags*/);
          	assertNotNull("virtual display must not be null", mVirtualDisplay);
          	waitForDisplayReady(mVirtualDisplay.getDisplay().getDisplayId());
          	return mVirtualDisplay.getDisplay();
          }
          
          • 上面的例子中创建虚拟屏,返回Display(实际上是VirtualDislay)对象。有了Display对象,我们就可以将View绑定到这个虚拟的Display上了(绑定网上方法比较多可自行搜索)。关于Surface的创建,有很多种方法,比如通过SurfaceContron+Buffer这种方式也可以。
          • VituralDisplay创建时,需要提供flag。其值定义如下,可通过 “或”将flag组合。
             
                public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1 
                    /**
                     * Called when the virtual display video projection has been
                     * paused by the system or when the surface has been detached
                     * by the application by calling setSurface(null).
                     * The surface will not receive any more buffers while paused.
                     */
                     public void onPaused() { }
                    /**
                     * Called when the virtual display video projection has been
                     * resumed after having been paused.
                     */
                     public void onResumed() { }
                    /**
                     * Called when the virtual display video projection has been
                     * stopped by the system.  It will no longer receive frames
                     * and it will never be resumed.  It is still the responsibility
                     * of the application to release() the virtual display.
                     */
                    public void onStopped() { }
                }
            
            	return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null);
            }
            public VirtualDisplay createVirtualDisplay(@NonNull String name,
            		int width, int height, int densityDpi, @Nullable Surface surface, int flags,
            		@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
            	final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
            			height, densityDpi);
            	builder.setFlags(flags);
            	if (surface != null) {
            		builder.setSurface(surface);
            	}
            	return createVirtualDisplay(null /* projection */, builder.build(), callback, handler);
            }
            // TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService)
            /** @hide */
            public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
            		@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
            		int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
            		@Nullable String uniqueId) {
            	final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
            			height, densityDpi);
            	builder.setFlags(flags);
            	if (uniqueId != null) {
            		builder.setUniqueId(uniqueId);
            	}
            	if (surface != null) {
            		builder.setSurface(surface);
            	}
            	return createVirtualDisplay(projection, builder.build(), callback, handler);
            }
            /** @hide */
            public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
            		@NonNull VirtualDisplayConfig virtualDisplayConfig,
            		@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
            	// 走的这里,会调用到DisplayManagerGlobal中。
            	return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback,
            			handler);
            }
            
            	VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
            	// 从MediaProjection过来的调用,这个地方非空。
            	IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
            	int displayId;
            	try {
            		// 告知DMS创建虚拟屏,并返回DisplayID
            		displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper,
            				projectionToken, context.getPackageName());
            	} catch (RemoteException ex) {
            		throw ex.rethrowFromSystemServer();
            	}
            	if (displayId 

免责声明
本网站所收集的部分公开资料来源于AI生成和互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
评论列表 (暂无评论,人围观)

还没有评论,来说两句吧...

目录[+]