Search code examples
androidkotlinscreenshotscreen-recordingandroid-mediaprojection

Capture screenshot during screen recording using MediaProjection API


I have a service class where I am capturing screen recording. I want to take a screenshot in this class, but I haven’t found any proper method to do so. Can anyone suggest a way to take a screenshot in a service class?

Here are mediaProjection and virtualDisplay objects intialize during my screen recording

mMediaProjection = (Objects.requireNonNull(
    getSystemService(
        MEDIA_PROJECTION_SERVICE
    )
) as MediaProjectionManager).getMediaProjection(
    mResultCode,
    mResultData!!
)

mVirtualDisplay = mMediaProjection!!.createVirtualDisplay(
    TAG,
    mScreenWidth,
    mScreenHeight,
    mScreenDensity,
    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
    mMediaRecorder!!.surface,
    null,
    null
)

This is my code for screenshot

var imageReader: ImageReader? = null // Initialize as needed

// Call this function to capture a screenshot
private fun captureScreenshot() {
    if (imageReader == null) {
        // Set up ImageReader to capture the screen content
        val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        val metrics = DisplayMetrics()
        windowManager.defaultDisplay.getMetrics(metrics)
        val screenDensity = metrics.densityDpi
        val screenWidth = metrics.widthPixels
        val screenHeight = metrics.heightPixels
        imageReader = ImageReader.newInstance(screenWidth, screenHeight, ImageFormat.RGB_565, 1)

        // Set the OnImageAvailableListener to handle new images
        imageReader!!.setOnImageAvailableListener({ reader ->
            // Process the captured image data
            val image = reader.acquireLatestImage()
            if (image != null) {
                try {
                    // Your image processing logic here
                    processImage(image)
                } finally {
                    // Always close the image to avoid resource leaks
                    image.close()
                }
            }
        }, Handler())
    }

}

private fun processImage(image: Image) {
    val planes = image.planes
    val buffer = planes[0].buffer
    val pixelStride = planes[0].pixelStride
    val rowStride = planes[0].rowStride
    val rowPadding = rowStride - pixelStride * image.width

    // Create a Bitmap to hold the screenshot
    val bitmap = Bitmap.createBitmap(
        image.width + rowPadding / pixelStride,
        image.height,
        Bitmap.Config.ARGB_8888
    )

    bitmap.copyPixelsFromBuffer(buffer)

    // Save the bitmap to a file (You may want to handle this more appropriately)
    saveBitmapToFile(bitmap)
}

private fun getParentFolder(): String {
    return "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)}/${
        getString(R.string.app_name)
    }"
}

private fun saveBitmapToFile(bitmap: Bitmap) {
    Log.d("de_save", "saveBitmapToFile: ")
    try {
        // Save the bitmap to a file (You may want to handle this more appropriately)
        val savePath = getFolder()
        Log.d("de_save", "saveBitmapToFile: savepath: $savePath")
        val file = File(savePath, "ScreenShot_${System.currentTimeMillis()}")
        val fileOutputStream = FileOutputStream(file)
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
        fileOutputStream.close()

        ToastUtils.showToast("Screenshot Taken")

    } catch (e: IOException) {
        e.printStackTrace()
    }
}

private fun getFolder(): String {
    val location = getParentFolder() + File.separator + "ScreenShot"
    val file = File(location)
    if (!file.exists()) {
        file.mkdirs()
    }
    return location
}

Using this code setOnImageAvailableListener is never called.


Solution

  • 你应该在createVirtualDisplay中使用ImageReader的surface。但是在Android14中会中断录屏。