From ed3caef9f328f6695eab36294512d95300f0db01 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Wed, 28 Oct 2020 18:55:14 -0400 Subject: [PATCH] Downscale recording as needed for device codecs Some devices cannot play back video at screen resolution, so check whether any codecs can support screen resolution, or downscale the video to the max supported size and refresh rate. Fixes: 167978946 Test: manual Change-Id: Ie174aa0db25f189c8e3601a81ad869777ba70a60 --- .../screenrecord/ScreenMediaRecorder.java | 98 +++++++++++++++++-- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index e6f43c1ff1d2e..45564b0bfa6b1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -30,6 +30,8 @@ import android.graphics.Bitmap; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.media.MediaFormat; import android.media.MediaMuxer; import android.media.MediaRecorder; import android.media.ThumbnailUtils; @@ -124,17 +126,19 @@ public class ScreenMediaRecorder { DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getRealMetrics(metrics); - int screenWidth = metrics.widthPixels; - int screenHeight = metrics.heightPixels; - int refereshRate = (int) wm.getDefaultDisplay().getRefreshRate(); - int vidBitRate = screenHeight * screenWidth * refereshRate / VIDEO_FRAME_RATE + int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate(); + int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate); + int width = dimens[0]; + int height = dimens[1]; + refreshRate = dimens[2]; + int vidBitRate = width * height * refreshRate / VIDEO_FRAME_RATE * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO; mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoEncodingProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, MediaCodecInfo.CodecProfileLevel.AVCLevel3); - mMediaRecorder.setVideoSize(screenWidth, screenHeight); - mMediaRecorder.setVideoFrameRate(refereshRate); + mMediaRecorder.setVideoSize(width, height); + mMediaRecorder.setVideoFrameRate(refreshRate); mMediaRecorder.setVideoEncodingBitRate(vidBitRate); mMediaRecorder.setMaxDuration(MAX_DURATION_MS); mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES); @@ -153,8 +157,8 @@ public class ScreenMediaRecorder { mInputSurface = mMediaRecorder.getSurface(); mVirtualDisplay = mMediaProjection.createVirtualDisplay( "Recording Display", - screenWidth, - screenHeight, + width, + height, metrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mInputSurface, @@ -172,6 +176,84 @@ public class ScreenMediaRecorder { } + /** + * Find the highest supported screen resolution and refresh rate for the given dimensions on + * this device, up to actual size and given rate. + * If possible this will return the same values as given, but values may be smaller on some + * devices. + * + * @param screenWidth Actual pixel width of screen + * @param screenHeight Actual pixel height of screen + * @param refreshRate Desired refresh rate + * @return array with supported width, height, and refresh rate + */ + private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) { + double maxScale = 0; + + MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + MediaCodecInfo.VideoCapabilities maxInfo = null; + for (MediaCodecInfo codec : codecList.getCodecInfos()) { + String videoType = MediaFormat.MIMETYPE_VIDEO_AVC; + String[] types = codec.getSupportedTypes(); + for (String t : types) { + if (!t.equalsIgnoreCase(videoType)) { + continue; + } + MediaCodecInfo.CodecCapabilities capabilities = + codec.getCapabilitiesForType(videoType); + if (capabilities != null && capabilities.getVideoCapabilities() != null) { + MediaCodecInfo.VideoCapabilities vc = capabilities.getVideoCapabilities(); + + int width = vc.getSupportedWidths().getUpper(); + int height = vc.getSupportedHeights().getUpper(); + + if (width >= screenWidth && height >= screenHeight + && vc.isSizeSupported(screenWidth, screenHeight)) { + + // Desired size is supported, now get the rate + int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight) + .getUpper().intValue(); + + if (maxRate < refreshRate) { + refreshRate = maxRate; + } + Log.d(TAG, "Screen size supported at rate " + refreshRate); + return new int[]{screenWidth, screenHeight, refreshRate}; + } + + // Otherwise, continue searching + double scale = Math.min(((double) width / screenWidth), + ((double) height / screenHeight)); + if (scale > maxScale) { + maxScale = scale; + maxInfo = vc; + } + } + } + } + + // Resize for max supported size + int scaledWidth = (int) (screenWidth * maxScale); + int scaledHeight = (int) (screenHeight * maxScale); + if (scaledWidth % maxInfo.getWidthAlignment() != 0) { + scaledWidth -= (scaledWidth % maxInfo.getWidthAlignment()); + } + if (scaledHeight % maxInfo.getHeightAlignment() != 0) { + scaledHeight -= (scaledHeight % maxInfo.getHeightAlignment()); + } + + // Find max supported rate for size + int maxRate = maxInfo.getSupportedFrameRatesFor(scaledWidth, scaledHeight) + .getUpper().intValue(); + if (maxRate < refreshRate) { + refreshRate = maxRate; + } + + Log.d(TAG, "Resized by " + maxScale + ": " + scaledWidth + ", " + scaledHeight + + ", " + refreshRate); + return new int[]{scaledWidth, scaledHeight, refreshRate}; + } + /** * Start screen recording */