硬件加速下Android WebView转Bitmap的替代实现方案
请问硬件加速开启场景下,如下WebView截帧代码的可用替代实现是什么?
myBitmap = Bitmap.createBitmap(1280, 720, Bitmap.Config.ARGB_8888); myWebView.layout(0, 0, 1280, 720); myWebView.setDrawingCacheEnabled(true); myWebView.buildDrawingCache(); canvas = new Canvas(myBitmap); myWebView.draw(canvas);
问题详情
我正在实现将WebView渲染内容传输到Unity的功能,通过int数组完成像素数据传递,目前使用webView.draw方法已实现普通页面内容的捕获,无需关注onCreate函数的代码结构,已知其写法较为零散。
onCreate阶段核心代码如下:
protected void onCreate(Bundle bundle) { super.onCreate(bundle); myWebView = new WebView(this); wcc = new WebChromeClient() {}; myWebView.setWebChromeClient(wcc); //setContentView(myWebView); myWebView.getSettings().setJavaScriptEnabled(true); myWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); CookieManager.getInstance().setAcceptThirdPartyCookies(myWebView, true); myWebView.setBackgroundColor(Color.argb(1, 0, 1, 0)); myWebView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { view.loadUrl(request.getUrl().toString()); } return super.shouldOverrideUrlLoading(view, request); } @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { super.onPageStarted(webview, url, favicon); webview.setVisibility(View.INVISIBLE); } @Override public void onPageFinished(WebView webview, String url) { webview.setVisibility(View.VISIBLE); super.onPageFinished(webview, url); } }); myWebView.getSettings().setUserAgentString("Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"); myWebView.setLayerType(View.LAYER_TYPE_HARDWARE, null); //HARDWARE getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); //HARDWARE myWebView.loadUrl("https://www.youtube.com/embed/QouNHuTnypQ"); System.loadLibrary("NativeCameraPlugin"); nativeInit(); setContext(this); _renderScript = RenderScript.create(this); _handlerThread = new HandlerThread(TAG); _handlerThread.start(); //startCamera(); }
以下requestJavaRendering函数由Unity侧调用,改造自Unity安卓原生相机插件,用于在Unity渲染线程循环获取WebView图像并更新到OpenGL纹理:
private void requestJavaRendering(int texturePointer) { if (!_update) { return; } int[] imageBuffer = new int[0]; if (myWebView != null) { imageBuffer = screenshot(myWebView); // 此处完成WebView到位图的转换 } if (imageBuffer.length > 1) { GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texturePointer); GLES30.glTexSubImage2D(GLES30.GL_TEXTURE_2D, 0, 0, 0, 1280, 720, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, IntBuffer.wrap(imageBuffer)); } }
当前故障现象
加载HTML5内容(如嵌入的YouTube视频)时,音频可正常播放,视频的暂停、静音、设置等控件也可正常显示,但视频画面为纯黑色。已尝试两种解决方案均未生效:
无法使用
setLayerType(View.LAYER_TYPE_SOFTWARE, null)关闭硬件加速——关闭后即使调用setContentView(myWebView)将WebView添加到视图层级,也无法正常渲染视频内容。
尝试方案1:调用webView.draw截图
使用通用的View截图方法实现,但无法正常渲染视频画面,仅能捕获UI控件。经排查,基于Bitmap创建的Canvas不支持硬件渲染,已尝试在AndroidManifest的Activity、Application节点配置android:hardwareAccelerated="true",同时在Unity侧、代码中全局开启硬件加速,但调用canvas.isHardwareAccelerated()始终返回false。
对应截图实现代码:
public int[] screenshot(final WebView webView) { try { myBitmap = Bitmap.createBitmap(1280, 720, Bitmap.Config.ARGB_8888); myWebView.layout(0, 0, 1280, 720); myWebView.setDrawingCacheEnabled(true); myWebView.buildDrawingCache(); canvas = new Canvas(myBitmap); myWebView.draw(canvas); IntBuffer intBuf = bitmapToRgba(myBitmap); int[] intArray = new int[intBuf.remaining()]; intBuf.get(intArray); return intArray; } catch (Exception e) { e.printStackTrace(); } catch (Throwable throwable) { throwable.printStackTrace(); } return null; }
尝试方案2:基于VirtualDisplay实现
尝试使用虚拟显示方案捕获渲染内容,但始终获取到null图像,且不确定代码的正确调用位置,测试代码如下:
ir = ImageReader.newInstance(1280, 720, PixelFormat.RGBA_8888, 4); DisplayManager dm = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); VirtualDisplay vd = dm.createVirtualDisplay("Test Display", 1280, 720, DisplayMetrics.DENSITY_DEFAULT, ir.getSurface(), DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION); Presentation presentation = new Presentation(myWebView.getContext(), vd.getDisplay()); presentation.show() Image img = ir.acquireLatestImage(); Image.Plane[] planes = img.getPlanes(); buff = planes[0].getBuffer().asIntBuffer();
已查阅Chromium官方WebView文档、StackOverflow相关问答、技术博客资料,但未找到可落地的实现步骤。
内容的提问来源于stack exchange,提问作者Warwlock

