Android 13媒体投影截图为黑色

2w2cym1i  于 2022-11-27  发布在  Android
关注(0)|答案(1)|浏览(286)

我有一个错误的媒体投影和截图,但只有与android 13,有时他们出来黑色,但并不总是。我曾试图把一个延迟(最多5秒),看看是否可能是Android系统的原因,但它仍然发生,任何帮助是感激。我确实搜索了网站,但没有出现与android 13的问题。

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

6tdlim6h1#

在我的测试和观察中,在Android 13上使用ImageReader和媒体投影时存在两个问题
首先,setOnImageAvailableListener的回调结果有时会返回空像素的缓冲区。

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()只是位图扩展:

fun Bitmap.isEmptyBitmap(): Boolean {
    val emptyBitmap = Bitmap.createBitmap(width, height, config)
    return this.sameAs(emptyBitmap)
}

第二个问题是,OnImageAvailableListener有时甚至不回调!在本例中,我设置了一个超时来等待结果,当超时时,从同一个MediaProjection示例重新创建VirtualDisplay对象,它就可以正常工作了。
我使用的是Kotlin协程,因此代码片段可能看起来像:

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

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

相关问题