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

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

问题描述

我的工作,需要捕捉的屏幕为位图传送的应用程序。我试图用全新的Andr​​oid 5.0 android.media.projection的API 中做屏幕捕捉。

的工作流程这个API的调用中的高潮,以

  mediaProjection.createVirtualDisplay(测试画面,宽度,高度,DPI,
   DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,面,NULL,NULL);
 

在我的这个捕捉初步尝试,我从一个SurfaceView来源曲面对象。这正常工作;最终的结果是显示器的一个微小的重复被绘制在屏幕上(导致德罗斯特效应

我想到了功能几乎完全,但我这时才发现的SurfaceViews是(从code的角度来看)不可读;你不能从他们那里得到一个位图。

在寻找其他的解决方案,我碰到这个问题其中有一个非常相似的目标矿井,并在该线程是建议使用SurfaceView的一个ImageReader而不是源传递到表面在createVirtualDisplay API调用。

然而,当我改变我的code使用的ImageReader,以代替SurfaceView我得到的运行时错误的logcat(无例外),并且回调函数的ImageReader不会被调用的。该createVirtualDisplay调用也返回一个看似有效的VirtualDisplay对象。

下面是logcat的:

  9230-9270 / com.android.techrocket9.nanoid E / BufferQueueProducer:[无名的-9230-0] dequeueBuffer:createGraphicBuffer失败
9230-9246 / com.android.techrocket9.nanoid E / BufferQueueProducer:[无名的-9230-0] dequeueBuffer:不能出列多个缓冲区没有设置缓冲计数
9230-9246 / com.android.techrocket9.nanoid E / BufferQueueProducer:[无名的-9230-0] dequeueBuffer:不能出列多个缓冲区没有设置缓冲计数
9230-9246 / com.android.techrocket9.nanoid E / BufferQueueProducer:[无名的-9230-0] dequeueBuffer:不能出列多个缓冲区没有设置缓冲计数
9230-9246 / com.android.techrocket9.nanoid E / BufferQueueProducer:[无名的-9230-0] dequeueBuffer:不能出列多个缓冲区没有设置缓冲计数
 

这是第二行重复〜100次停止发生之前。

在调试器步进通过我看到createVirtualDisplay通话过程中出现的第一个错误,并执行返回到系统code后,所有的人发生一些问题。

href="https://$c$c.google.com/p/android/issues/detail?id=63738"此错误rel="nofollow">唯一有意义的结果的弄不好建议这里 (把安卓hardwareAccelerated =假在清单)。这并没有改变应用程序的行为。

我如何设置缓冲区数或以其他方式解决这个错误,并得到屏幕为位图?

P.S。我的开发平台是Nexus的6.我会标记这个问题本身,但标签尚不存在,我没有代表做出来。

满code座的要求:

  MediaProjection mediaProjection = mgr.getMediaProjection(结果code,数据);
的ImageReader IR = ImageReader.newInstance(宽度,高度,ImageFormat.JPEG,5);
VirtualDisplay V = mediaProjection.createVirtualDisplay(测试画面,宽度,高度,getApplicationContext()getResources()getDisplayMetrics()densityDpi,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,ir.getSurface(),NULL,NULL。);
 

编辑:关于神器的问题,这里是code,我用得到位图缩小图像,并显示它:

 公共无效onImageAvailable(的ImageReader阅读器){
        形象画像= NULL;
        ByteArrayOutputStream BOS = NULL;

        尝试 {
            图像= reader.acquireLatestImage();
            如果(空==图像){
                返回;
            }
            BOS =新ByteArrayOutputStream();
            最后Image.Plane []飞机= image.getPlanes();
            。最后的ByteBuffer缓冲=(ByteBuffer的)飞机[0] .getBuffer()快退();
            最后的位图位= Bitmap.createBitmap(image.getWidth(),image.getHeight(),Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(缓冲液);
            //bitmap.com$p$pss(Bitmap.Com$p$pssFormat.WEBP,50,BOS);

            runOnUiThread(新的Runnable(){
                公共无效的run(){
                    iv.setImageBitmap(位);
                }
            });
 

解决方案

我想我现在可以回答这个问题,我遇到了同样的问题后,我改变 ImageFormat.JPEG PixelFormat.RGBA_8888 一切顺利。这似乎是不支持ImageFormat.JPEG。

您需要使用下面的code,以获得正确的位图:

  INT宽度= img.getWidth();
                    INT高= img.getHeight();
                    INT pixelStride =平面[0] .getPixelStride();
                    INT rowStride =平面[0] .getRowStride();
                    INT rowPadding = rowStride  -  pixelStride *宽;
                    byte []的newData =新的字节[宽*高* 4]。

                    INT偏移= 0;
                    位= Bitmap.createBitmap(度量,宽度,高度,Bitmap.Config.ARGB_8888);
                    ByteBuffer的缓冲=平面[0] .getBuffer();
                    的for(int i = 0; I<高度; ++ I){
                        对于(INT J = 0; J<宽度; ++ j)条{
                            INT像素= 0;
                            像素| =(buffer.get(偏移)及0xff的)所述;&其中; 16; //ř
                            像素| =(buffer.get(偏移+ 1)安培; 0xff的)所述;&其中; 8; // G
                            像素| =(buffer.get(偏移+ 2)安培; 0xff的); // B
                            像素| =(buffer.get(偏移+ 3)及0xff的)所述;&其中; 24; // 一个
                            bitmap.setPixel(J,I,像素);
                            胶印+ = pixelStride;
                        }
                        胶印+ = rowPadding;
                    }
 

这是这种方式,位图的内容是你想要的。

PS:我真的很想说,机器人的文档是pretty的坏。我们需要研究太多细节正确地使用SDK API。

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.

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);

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)

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.

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.

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.

Here is the logcat:

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

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

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.

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?

P.S. My development platform is the Nexus 6. I would have tagged the question as such, but the tag doesn't exist yet and I don't have the rep to make it.

The full code block, as requested:

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);

Edit: 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);
                }
            });

解决方案

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: 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天全站免登陆