首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android 13媒体投影截图是黑色的

Android 13媒体投影截图是黑色的
EN

Stack Overflow用户
提问于 2022-10-16 15:25:27
回答 1查看 155关注 0票数 1

我在媒体的投影和截图上有一个错误,但只对android 13,有时它们是黑色的,但并不总是如此。我试着延迟一下(最多5秒),看看android系统是否是造成这种情况的原因,但它还是会发生的,任何帮助都是值得感谢的。我确实搜索了这个网站,但是没有出现android 13的问题。

代码语言:javascript
复制
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getRealSize(size);
final int width = size.x, height = size.y;

final ImageReader imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1);

imageReader.setOnImageAvailableListener(reader -> {
    //-> Stop our media projection just in case it is running
    mediaProjection.stop();

    Image image = reader.acquireLatestImage();
    if (image != null){
      Image.Plane[] planes = image.getPlanes();
      ByteBuffer buffer = planes[0].getBuffer();
      int pixelStride = planes[0].getPixelStride(), rowStride = planes[0].getRowStride(), rowPadding = rowStride - pixelStride * width;
      bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
      bitmap.copyPixelsFromBuffer(buffer); 

      String fileName = "Screenshot_" + System.currentTimeMillis() + ".jpg";
      String destinationPath = this.getExternalFilesDir(null) + "/screenshots/" + fileName;

      File imageFile = new File(destinationPath);
      FileOutputStream outputStream = new FileOutputStream(imageFile);
      int quality = 100;
      bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
      outputStream.flush();
      outputStream.close();

      String mediaPath = Environment.DIRECTORY_PICTURES + File.separator + "Screenshots/myapp" + File.separator;

      ContentValues values = new ContentValues();
      values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
      values.put(MediaStore.Images.Media.IS_PENDING, 0);
      values.put(MediaStore.Images.Media.RELATIVE_PATH, mediaPath);
      values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
      values.put(MediaStore.Images.Media.SIZE, imageFile.length());
      values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
      Uri path = this.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

      OutputStream imageOutStream = this.getContentResolver().openOutputStream(path);

      bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream);
      if (imageOutStream != null) {
         imageOutStream.flush();
         imageOutStream.close();
      }

      if (image != null) { image.close(); }
      mediaProjection.stop();
      if (reader != null){ reader.close(); }
    }
}, null);
EN

回答 1

Stack Overflow用户

发布于 2022-11-09 02:13:38

在我的测试和观察中,当在Android13上使用ImageReader和媒体投影时,有两个问题

首先,setOnImageAvailableListener的回调结果有时会返回带空像素的缓冲区。因此,我们只需等待下一个图像,直到得到一个非空的位图。

代码语言:javascript
复制
imageReader.setOnImageAvailableListener({ imageReader ->
    val image = imageReader.acquireLatestImage()
    // ... get buffer here
    val bitmap = Bitmap.createBitmap(screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888)
    bitmap.copyPixelsFromBuffer(buffer)

    // IMPORTANT!
    val isEmptyBitmap = bitmap.isEmptyBitmap()
    if (isEmptyBitmap) {
        // don't stop the listener and let the imageReader continue to run so that we can get next round of image buffer
    } else {
        // save bitmap to file
    }

    }, Handler(Looper.getMainLooper()))

isEmptyBitmap()只是位图的一个扩展:

代码语言:javascript
复制
fun Bitmap.isEmptyBitmap(): Boolean {
    val emptyBitmap = Bitmap.createBitmap(width, height, config)
    return this.sameAs(emptyBitmap)
}

第二个问题是,OnImageAvailableListener有时甚至不回调!在本例中,我设置了一个等待结果的超时,当超时时,从同一个VirtualDisplay实例重新创建MediaProjection对象,它就可以工作了。

我使用的是kotlin协同器,因此代码片段看起来可能如下:

代码语言:javascript
复制
retry(10) {
    val imageVirtualDisplay = createVirtualDisplay(...)
    try {
        withTimeout(100) {
            // awaitImageAvailable will suspend here to get the non-empty bitmap
            val bitmap = imageReader.awaitImageAvailable(screenWidth, screenHeight)
            // save bitmap to file
        }
    } finally {
        Timber.d("screenshot imageVirtualDisplay release")
        imageVirtualDisplay?.release()
    }
}

awaitImageAvailable的实现

代码语言:javascript
复制
suspend fun ImageReader.awaitImageAvailable(screenWidth: Int, screenHeight: Int): Bitmap =
suspendCancellableCoroutine { cont ->
    setOnImageAvailableListener({ imageReader ->

        val image = imageReader.acquireLatestImage() ?: throw Error("get screen image result failed")

        val planes = image.planes
        val buffer = planes[0].buffer

        val pixelStride = planes[0].pixelStride
        val rowStride = planes[0].rowStride
        val rowPadding: Int = rowStride - pixelStride * screenWidth
        var bitmap =
            Bitmap.createBitmap(screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888)
        bitmap.copyPixelsFromBuffer(buffer)
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, screenWidth, screenHeight)
        image.close()

        if (!bitmap.isEmptyBitmap()) {
            Timber.d("image not empty")
            setOnImageAvailableListener(null, null)
            cont.resume(bitmap)
        } else {
            Timber.w("image empty")
        }
    }, Handler(Looper.getMainLooper()))

    cont.invokeOnCancellation { setOnImageAvailableListener(null, null) }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74088324

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档