将 MediaProjection 虚拟显示的输出捕获到 ImageReader 的系统错误 [英] System error capturing the output of a MediaProjection virtual display to an ImageReader

查看:34
本文介绍了将 MediaProjection 虚拟显示的输出捕获到 ImageReader 的系统错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个需要将屏幕捕获到位图以进行传输的应用程序.我正在尝试使用新的 Android 5.0 android.media.projectionAPI 进行屏幕截图.

I am working on an application that needs to capture the screen to a bitmap to transmit. I am attempting to use the new Android 5.0 android.media.projection APIs to do the screen capture.

此 API 的工作流程以调用

The workflow for this API culminates in a call to

mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, DPI,
   DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null);

在我最初尝试捕获时,我从 SurfaceView 获取表面对象.这工作正常;最终结果是在屏幕上绘制的显示的微小副本(导致 Droste Effect)

In my initial attempt at this capture I sourced the surface object from a SurfaceView. This works correctly; the end result is a tiny duplicate of the display being drawn on-screen (resulting in a Droste Effect)

我认为该功能几乎已完成,但然后我发现 SurfaceViews(从代码的角度来看)不可读;你不能从他们那里得到位图.

I thought the feature nearly complete, but I then discovered that SurfaceViews are (from a code standpoint) not readable; you cannot get a bitmap from them.

在寻找其他解决方案时,我遇到了这个问题,其中有一个非常相似的我的目标,在该线程中建议使用 ImageReader 而不是 SurfaceView 来获取您传递给的 SurfacecreateVirtualDisplay API 调用.

In looking for other solutions I came across this question which has a very similar goal to mine, and in that thread it is suggested to use an ImageReader instead of a SurfaceView to source the Surface that you pass to the createVirtualDisplay API call.

但是,当我更改代码以使用 ImageReader 代替 SurfaceView 时,我收到运行时 logcat 错误(无异常),并且永远不会调用 ImageReader 的回调函数.createVirtualDisplay 调用还返回一个看似有效的 VirtualDisplay 对象.

However, when I change my code to use an ImageReader in lieu of a SurfaceView I get runtime logcat errors (no exceptions), and the callback function for the ImageReader never gets called. The createVirtualDisplay call also returns a seemingly valid VirtualDisplay object.

这里是日志:

9230-9270/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: createGraphicBuffer failed
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count
9230-9246/com.android.techrocket9.nanoid E/BufferQueueProducer﹕ [unnamed-9230-0] dequeueBuffer: can't dequeue multiple buffers without setting the buffer count

第二行在它停止出现之前重复了大约 100 次.

That second line repeats ~100 times before it stops occurring.

逐步调试调试器,我看到第一个错误发生在 createVirtualDisplay 调用期间,所有其他错误发生在执行返回系统代码后的某个时间点.

Stepping through on the debugger I see that the first error occurs during the createVirtualDisplay call, and all the others happen some point after execution returns to system code.

此错误的唯一有意义的结果与Kitkat 中的一个问题,我尝试使用的 API 不存在.尽管如此,我还是尝试了 此处建议的修复(将android:hardwareAccelerated="false" 在清单中).这并没有改变应用程序的行为.

The only meaningful result for this error relates to an issue in Kitkat, where the API I am trying to consume does not exist. Nonetheless, I tried the fix suggested here (putting android:hardwareAccelerated="false" in the manifest). This did not change the application's behavior.

如何设置缓冲区计数"或以其他方式解决此错误并将屏幕显示为位图?

How can I "set the buffer count" or otherwise work around this error and get the screen as a bitmap?

附言我的开发平台是 Nexus 6.

P.S. My development platform is the Nexus 6.

请求的完整代码块:

MediaProjection mediaProjection = mgr.getMediaProjection(resultCode, data);
ImageReader ir = ImageReader.newInstance(WIDTH, HEIGHT, ImageFormat.JPEG, 5);
VirtualDisplay v = mediaProjection.createVirtualDisplay("Test Screen", WIDTH, HEIGHT, getApplicationContext().getResources().getDisplayMetrics().densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, ir.getSurface(), null, null);

关于工件问题,这是我用来从图像中获取位图并显示它的代码:

Regarding the artifact issue, here is the code I am using to get the bitmap out of the image and display it:

 public void onImageAvailable(ImageReader reader) {
        Image image = null;
        ByteArrayOutputStream bos = null;

        try {
            image = reader.acquireLatestImage();
            if (null == image){
                return;
            }
            bos = new ByteArrayOutputStream();
            final Image.Plane[] planes = image.getPlanes();
            final ByteBuffer buffer = (ByteBuffer) planes[0].getBuffer().rewind();
            final Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(buffer);
            //bitmap.compress(Bitmap.CompressFormat.WEBP, 50, bos);

            runOnUiThread(new Runnable() {
                public void run() {
                    iv.setImageBitmap(bitmap);
                }
            });

推荐答案

我觉得我现在可以回答这个问题了,我遇到了同样的问题,在我将 ImageFormat.JPEG 更改为 PixelFormat 之后.RGBA_8888 一切顺利.似乎不支持 ImageFormat.JPEG.

I think I can answer this question now, I met the same problem and after I change ImageFormat.JPEG to PixelFormat.RGBA_8888 everything goes well. It seems ImageFormat.JPEG is not supported.

您需要使用以下代码来获取正确的位图:

You need to use the following code to get the correct bitmap:

                    int width = img.getWidth();
                    int height = img.getHeight();
                    int pixelStride = planes[0].getPixelStride();
                    int rowStride = planes[0].getRowStride();
                    int rowPadding = rowStride - pixelStride * width;
                    byte[] newData = new byte[width * height * 4];

                    int offset = 0;
                    bitmap = Bitmap.createBitmap(metrics,width, height, Bitmap.Config.ARGB_8888);
                    ByteBuffer buffer = planes[0].getBuffer();
                    for (int i = 0; i < height; ++i) {
                        for (int j = 0; j < width; ++j) {
                            int pixel = 0;
                            pixel |= (buffer.get(offset) & 0xff) << 16;     // R
                            pixel |= (buffer.get(offset + 1) & 0xff) << 8;  // G
                            pixel |= (buffer.get(offset + 2) & 0xff);       // B
                            pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
                            bitmap.setPixel(j, i, pixel);
                            offset += pixelStride;
                        }
                        offset += rowPadding;
                    }

这样一来,位图的内容就是你想要的了.

From this way, the content of bitmap is what you want.

PS:我真的很想说,android的文档很糟糕.我们需要调查太多细节才能正确使用 sdk api.

PS: I really want to say, the doc of android is pretty bad. we need to investigate too much detail to use sdk api correctly.

这篇关于将 MediaProjection 虚拟显示的输出捕获到 ImageReader 的系统错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆