Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比

场景:

直播设置采集宽高 360p 360x640 但是手机相机不支持,所以出现裁剪(脸大问题)

解决方案:

获取手机相机支持的宽高尺寸 ,根据需要设置的宽高动态匹配接近的宽高比 阈值要设置好

arduino 复制代码
阀值 0.2
val size = CameraSizeHelper.getInstance().getOptimalSize(context, config.captureWidth, config.captureHeight)
// 接近的宽高
val w=size.getWidth()

val w=size.getHeight()
ini 复制代码
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.*;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Log;
import android.util.Size;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * 改进版 CameraSizeHelper
 * 支持向上枚举 + 比例误差阈值可配置
 */
public class CameraSizeHelper {

    private static volatile CameraSizeHelper instance;

    private CameraSizeHelper() {}

    public static CameraSizeHelper getInstance() {
        if (instance == null) {
            synchronized (CameraSizeHelper.class) {
                if (instance == null) {
                    instance = new CameraSizeHelper();
                }
            }
        }
        return instance;
    }

    /**
     * 获取最接近目标尺寸的相机预览尺寸
     * @param context 上下文
     * @param targetWidth 目标宽度
     * @param targetHeight 目标高度
     * @param ratioThreshold 比例误差阈值,例如0.2
     * @return 最合适的支持尺寸
     */
    public Size getOptimalSize(Context context, int targetWidth, int targetHeight, double ratioThreshold) {
        try {
            CameraManager cm = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
            String cameraId = null;

            // 找后置摄像头
            for (String id : cm.getCameraIdList()) {
                CameraCharacteristics characteristics = cm.getCameraCharacteristics(id);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
                    cameraId = id;
                    break;
                }
            }
            if (cameraId == null) return new Size(targetWidth, targetHeight);

            CameraCharacteristics cc = cm.getCameraCharacteristics(cameraId);
            StreamConfigurationMap map = cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            if (map == null) return new Size(targetWidth, targetHeight);

            Size[] sizes = map.getOutputSizes(SurfaceTexture.class);
            if (sizes == null || sizes.length == 0) return new Size(targetWidth, targetHeight);

            boolean portrait = targetHeight > targetWidth;
            double targetRatio = (double) targetHeight / targetWidth;

            // 0. 完全匹配优先
            for (Size s : sizes) {
                int w = portrait && s.getWidth() > s.getHeight() ? s.getHeight() : s.getWidth();
                int h = portrait && s.getWidth() > s.getHeight() ? s.getWidth() : s.getHeight();
                if (w == targetWidth && h == targetHeight) {
                    return new Size(w, h);
                }
            }

            // 1. 将所有尺寸按宽度升序排列(竖屏)
            List<Size> sortedSizes = new ArrayList<>();
            for (Size s : sizes) {
                int w = portrait && s.getWidth() > s.getHeight() ? s.getHeight() : s.getWidth();
                int h = portrait && s.getWidth() > s.getHeight() ? s.getWidth() : s.getHeight();
                sortedSizes.add(new Size(w, h));
            }
            sortedSizes.sort(Comparator.comparingInt(Size::getWidth));

            // 2. 从目标宽度向上枚举
            Size best = null;
            double minDiff = Double.MAX_VALUE;
            for (int i = 0; i < sortedSizes.size(); i++) {
                Size s = sortedSizes.get(i);
                if (s.getWidth() < targetWidth) continue; // 向上枚举
                double ratio = (double) s.getHeight() / s.getWidth();
                double diff = Math.abs(ratio - targetRatio);
                if (diff <= ratioThreshold) { // 阈值内直接返回
                    return s;
                } else {
                    // 记录最小比例差的候选
                    if (diff < minDiff) {
                        minDiff = diff;
                        best = s;
                    }
                }
            }

            // 3. 如果向上找不到阈值内的,返回最小比例差的向上尺寸
            if (best != null) return best;

            // 4. 否则返回最大尺寸(兜底)
            return sortedSizes.get(sortedSizes.size() - 1);

        } catch (Exception e) {
            Log.e("CameraSizeHelper", "getOptimalSize failed", e);
            return new Size(targetWidth, targetHeight);
        }
    }

    /**
     * 默认阈值调用
     */
    public Size getOptimalSize(Context context, int targetWidth, int targetHeight) {
        return getOptimalSize(context, targetWidth, targetHeight, 0.2);
    }
}
```
```
相关推荐
shenshizhong2 小时前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强2 小时前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸2 小时前
【底层机制】 Android ION内存分配器深度解析
android·面试
你听得到113 小时前
肝了半个月,我用 Flutter 写了个功能强大的图片编辑器,告别image_cropper
android·前端·flutter
KevinWang_3 小时前
Android 原生 app 和 WebView 如何交互?
android
用户69371750013843 小时前
Android Studio中Gradle、AGP、Java 版本关系:不再被构建折磨!
android·android studio
杨筱毅4 小时前
【底层机制】Android低内存管理机制深度解析
android·底层机制
二流小码农4 小时前
鸿蒙开发:this的指向问题
android·ios·harmonyos
循环不息优化不止5 小时前
Jetpack Compose 状态管理
android