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);
    }
}
```
```
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui