【问题标题】:Android - Get byte[] from Surface / virtualDisplayAndroid - 从 Surface / virtualDisplay 获取字节 []
【发布时间】:2016-11-17 04:35:23
【问题描述】:

我正在尝试使用 projection APIWebrtc 将我的 android 设备屏幕投射到网络浏览器。

Projection API 将其输出呈现为 Surface 并返回一个 virtualDisplay。我已经做到了。我看到了 android 的 webrtc 库。他们已经让它只接收来自设备相机的输入。我正在尝试读取和修改 webrtc 代码以流式传输任何显示到表面的内容。

我的问题是我怎样才能像Camera.PreviewCallback 函数一样定期从surface 接收字节[] 数据。我还有哪些其他可用选项?

【问题讨论】:

  • 如果框架很小,您可以将它们整个传递。如果它们不常见,您可以将每一个转换为 JPEG 或 PNG。如果您想要接近显示刷新率的东西,则需要使用视频编码器将它们转换为视频流。您的问题对于 stackoverflow 来说有点含糊——这个网站是针对特定的编程问题而设计的。
  • @fadden 你能看看这个问题吗:stackoverflow.com/questions/31183009/…我只需要你说是或否。
  • @fadden 我已经更新了这个问题。请提供您的评论。如果我想错了方向。请指导我正确的方向。
  • 理论上您可以使用 ImageReader Surface (developer.android.com/reference/android/media/ImageReader.html),但我不知道它是否适用于 RGB888 表面(它适用于相机 YUV 输出)。您可能需要在 SurfaceTexture 表面上接收帧,将它们渲染到 pbuffer,然后使用glReadPixels() 提取它们。你需要byte[]吗?根据您尝试执行的操作,可能有一种方法可以让 GPU 执行此操作。
  • 之字形像素通常是忽略步幅的结果——确保您使用的是getPixelStride()getRowStride()。 ImageReader 是使用 CPU 访问 Surface 内容的最快、最简单的方法。另一种方法是执行bigflake.com/mediacodec/#ExtractMpegFramesTest 之类的操作,它将视频发送到 SurfaceTexture,对其进行渲染,然后使用glReadPixels() 提取像素。

标签: android webrtc surface android-mediaprojection


【解决方案1】:

这是我解决问题的方法。我像使用 ImageReader 类

imageReader = ImageReader.newInstance(displayWidth, displayHeight, PixelFormat.RGBA_8888, 2);
mediaProjection.createVirtualDisplay("screencapture",
                    displayWidth, displayHeight, density,
                    flags, imageReader.getSurface(), null, handler);
imageReader.setOnImageAvailableListener(new ImageAvailableListener(), null);


private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Image image = null;
        Bitmap bitmap = null;

        ByteArrayOutputStream stream = null;

        try {
            image = imageReader.acquireLatestImage();
            if (image != null) {
                Image.Plane[] planes = image.getPlanes();
                ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * displayWidth;

                // create bitmap
                bitmap = Bitmap.createBitmap(displayWidth + rowPadding / pixelStride,
                        displayHeight, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);
                    stream = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream);
                    StringBuilder sb = new StringBuilder();
                    sb.append("data:image/png;base64,");
                    sb.append(StringUtils.newStringUtf8(Base64.encode(stream.toByteArray(), Base64.DEFAULT)));
                WebrtcClient.sendProjection(sb.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

我正在将 byte[] 转换为 Base64 字符串并通过 webrtc 数据通道发送。

【讨论】:

  • 在这种情况下,ImageReader 的 FPS 性能如何?为什么选择通过数据通道而不是常规视频通道发送流?
  • @MichaelP ImageReader 的性能相当不错。如果您不想处理 surfaceTexture,那么 ImageReader 是从表面获取 byte[] 的最快方法。我在这个项目中使用了 webrtc-android sdk。他们有硬编码的视频通道可以与设备摄像头一起使用。我尝试通过编辑他们的源代码来改变这种行为,但收效甚微。上述解决方案对我来说非常有效。
  • @Shubenda,感谢您的回复。我实际上正在尝试做同样的事情,我今天整天都在试图弄清楚如何修改他们的代码来做到这一点。这是我的新问题stackoverflow.com/questions/32084067/… 他们确实将 Java 包装器与 C++ 实现紧密结合。您提到了 SurfaceTexture,我不认为 SurfaceTexture 可以给您输入 Surface(或获取表面),因此似乎唯一的解决方案是使用 ImageReader。我只是想确保它不像调用 glReadPixels 那样慢,它可以做到 30 FPS
  • @shubendrak:我在同一条船上,但不使用数据通道。可以请代码吗?
  • 您能帮我了解用于捕获屏幕的库吗?以及有关接收 Base64 流的 webRTC 库的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 2021-06-29
  • 1970-01-01
相关资源
最近更新 更多