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);
    }
}
```
```
相关推荐
帅得不敢出门1 分钟前
Android定位RK编译的system.img比MTK大350M的原因
android·framework·策略模式
darkb1rd16 分钟前
三、PHP字符串处理与编码安全
android·安全·php
STCNXPARM10 小时前
Android camera之硬件架构
android·硬件架构·camera
2501_9445255411 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
松☆13 小时前
Dart 核心语法精讲:从空安全到流程控制(3)
android·java·开发语言
_李小白14 小时前
【Android 美颜相机】第二十三天:GPUImageDarkenBlendFilter(变暗混合滤镜)
android·数码相机
小天源17 小时前
银河麒麟 V10(x86_64)离线安装 MySQL 8.0
android·mysql·adb·麒麟v10
2501_9159214317 小时前
傻瓜式 HTTPS 抓包,简单抓取iOS设备数据
android·网络协议·ios·小程序·https·uni-app·iphone
csj5018 小时前
安卓基础之《(20)—高级控件(2)列表类视图》
android
JMchen12318 小时前
Android计算摄影实战:多帧合成、HDR+与夜景算法深度剖析
android·经验分享·数码相机·算法·移动开发·android-studio