package com.genymobile.scrcpy.util; import com.genymobile.scrcpy.wrappers.DisplayManager; import com.genymobile.scrcpy.wrappers.ServiceManager; import android.annotation.TargetApi; import android.graphics.Rect; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.MediaCodec; import android.os.Build; import android.util.Range; import java.util.Arrays; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; public final class LogUtils { private LogUtils() { // not instantiable } public static String buildVideoEncoderListMessage() { StringBuilder builder = new StringBuilder("List of video encoders:"); List videoEncoders = CodecUtils.listVideoEncoders(); if (videoEncoders.isEmpty()) { builder.append("\n (none)"); } else { for (CodecUtils.DeviceEncoder encoder : videoEncoders) { builder.append("\n --video-codec=").append(encoder.getCodec().getName()); builder.append(" --video-encoder='").append(encoder.getInfo().getName()).append("'"); } } return builder.toString(); } public static String buildAudioEncoderListMessage() { StringBuilder builder = new StringBuilder("List of audio encoders:"); List audioEncoders = CodecUtils.listAudioEncoders(); if (audioEncoders.isEmpty()) { builder.append("\n (none)"); } else { for (CodecUtils.DeviceEncoder encoder : audioEncoders) { builder.append("\n --audio-codec=").append(encoder.getCodec().getName()); builder.append(" --audio-encoder='").append(encoder.getInfo().getName()).append("'"); } } return builder.toString(); } public static String buildDisplayListMessage() { StringBuilder builder = new StringBuilder("List of displays:"); DisplayManager displayManager = ServiceManager.getDisplayManager(); int[] displayIds = displayManager.getDisplayIds(); if (displayIds == null || displayIds.length == 0) { builder.append("\n (none)"); } else { for (int id : displayIds) { builder.append("\n --display-id=").append(id).append(" ("); //DisplayInfo displayInfo = displayManager.getDisplayInfo(id); builder.append(")"); } } return builder.toString(); } private static String getCameraFacingName(int facing) { switch (facing) { case CameraCharacteristics.LENS_FACING_FRONT: return "front"; case CameraCharacteristics.LENS_FACING_BACK: return "back"; case CameraCharacteristics.LENS_FACING_EXTERNAL: return "external"; default: return "unknown"; } } @TargetApi(Build.VERSION_CODES.P) public static String buildCameraListMessage(boolean includeSizes) { StringBuilder builder = new StringBuilder("List of cameras:"); CameraManager cameraManager = ServiceManager.getCameraManager(); try { String[] cameraIds = cameraManager.getCameraIdList(); if (cameraIds == null || cameraIds.length == 0) { builder.append("\n (none)"); } else { for (String id : cameraIds) { builder.append("\n --camera-id=").append(id); CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); int facing = characteristics.get(CameraCharacteristics.LENS_FACING); builder.append(" (").append(getCameraFacingName(facing)).append(", "); Rect activeSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); builder.append(activeSize.width()).append("x").append(activeSize.height()).append(", "); // Capture frame rates for low-FPS mode are the same for every resolution Range[] lowFpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); SortedSet uniqueLowFps = getUniqueSet(lowFpsRanges); builder.append("fps=").append(uniqueLowFps).append(')'); int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); if (contains(capabilities, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) { builder.append("\n Logical multi-camera, backed by physical cameras:"); for (String phyId : characteristics.getPhysicalCameraIds()) { CameraCharacteristics phyCharacteristics = cameraManager.getCameraCharacteristics(phyId); appendPhysicalCamera(builder, phyId, phyCharacteristics); if (includeSizes) { appendSizes(builder, phyCharacteristics); } } } else if (includeSizes) { appendSizes(builder, characteristics); } } } } catch (CameraAccessException e) { builder.append("\n (access denied)"); } return builder.toString(); } public static boolean contains(int[] array, int value) { for (int i : array) { if (i == value) { return true; } } return false; } private static void appendPhysicalCamera(StringBuilder builder, String id, CameraCharacteristics characteristics) { builder.append("\n --camera-id=").append(id).append(" ("); Rect activeSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); builder.append(activeSize.width()).append("x").append(activeSize.height()).append(")"); } private static void appendSizes(StringBuilder builder, CameraCharacteristics characteristics) { StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); android.util.Size[] sizes = configs.getOutputSizes(MediaCodec.class); for (android.util.Size size : sizes) { builder.append("\n - ").append(size.getWidth()).append('x').append(size.getHeight()); } android.util.Size[] highSpeedSizes = configs.getHighSpeedVideoSizes(); if (highSpeedSizes.length > 0) { builder.append("\n High speed capture (--camera-high-speed):"); for (android.util.Size size : highSpeedSizes) { Range[] highFpsRanges = configs.getHighSpeedVideoFpsRanges(); SortedSet uniqueHighFps = getUniqueSet(highFpsRanges); builder.append("\n - ").append(size.getWidth()).append("x").append(size.getHeight()); builder.append(" (fps=").append(uniqueHighFps).append(')'); } } } private static SortedSet getUniqueSet(Range[] ranges) { SortedSet set = new TreeSet<>(); for (Range range : ranges) { set.add(range.getUpper()); } return set; } }