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);
    }
}
```
```
相关推荐
hunterandroid16 分钟前
Android ViewModel + LiveData 工程实践:配置变更、一次性事件与状态恢复
android
2501_915921431 小时前
iOS应用从开发到上架App Store的完整步骤详解
android·ios·小程序·https·uni-app·iphone·webview
HackTwoHub2 小时前
免 Root 安卓渗透工具 Stryker|把 Kali 装进手机!随身无线渗透工作站,内置 Nmap/Nuclei/Metasploit 全套武器库
android·linux·运维·人工智能·web安全·macos·智能手机
朱涛的自习室12 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
.Bryce.16 小时前
在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“
android·java·服务器·redis·后端·agent·a2a
方舟小孩17 小时前
开源办公软件海豹办公 1.9.15 实战 从模板异常到 Android WebView 资源回归
android
用户21816970493017 小时前
Kotlin 语法四 协程
android
出师牛17 小时前
Android - 再探Handler
android
高晶17 小时前
Android 物联网组网实战:NFC 从零到落地
android·java
stevenzqzq17 小时前
android 维度和渠道
android