将 MediaProjection 虚拟显示的输出捕获到 ImageReader 的系统错误 [英] System error capturing the output of a MediaProjection virtual display to an 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屋!