You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Camera录制指定尺寸视频时预览画面变形问题求助

解决自定义Camera预览画面拉伸变形问题

这个问题我之前做相机应用时也碰到过,核心原因是预览控件(SurfaceView/TextureView)的显示比例没有和相机预览尺寸的比例保持一致。你已经选对了相机支持的视频/预览尺寸,但如果Surface的宽高比和这个尺寸的比例不匹配,系统就会强制拉伸画面适配,最终导致变形。下面是具体的解决方案:

1. 确保选中的相机尺寸比例正确

首先优化你获取支持视频尺寸的逻辑,优先匹配4:3比例(640×480是标准4:3)的尺寸,而不是只看宽度:

Camera.Size useMe = null;
int targetWidth = 640;
int targetHeight = 480;
float targetRatio = (float) targetWidth / targetHeight;
int minSizeDiff = Integer.MAX_VALUE;

Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> supportedSizes = parameters.getSupportedVideoSizes();
// 如果设备不单独返回视频尺寸,用预览尺寸替代
if (supportedSizes == null) {
    supportedSizes = parameters.getSupportedPreviewSizes();
}

// 优先找比例匹配、尺寸最接近640×480的选项
for (Camera.Size size : supportedSizes) {
    float currentRatio = (float) size.width / size.height;
    // 允许0.01的比例误差,避免设备比例精度问题
    if (Math.abs(currentRatio - targetRatio) < 0.01) {
        int diff = Math.abs(size.width - targetWidth) + Math.abs(size.height - targetHeight);
        if (diff < minSizeDiff) {
            minSizeDiff = diff;
            useMe = size;
        }
    }
}

// 找不到比例匹配的,再用原逻辑降级处理
if (useMe == null) {
    for (int i = 0; i < supportedSizes.size(); i++) {
        Camera.Size size = supportedSizes.get(i);
        if (size.width == targetWidth) {
            useMe = size;
            break;
        }
        if (size.width < targetWidth) {
            useMe = supportedSizes.get(Math.max(0, i - 1));
            break;
        }
    }
    if (useMe == null) {
        useMe = supportedSizes.get(supportedSizes.size() / 2);
    }
}

2. 让预览控件保持和相机尺寸一致的比例

在surfaceChanged方法中,不要直接给Surface设置固定尺寸,而是根据相机预览尺寸的比例,动态调整Surface的宽高,确保比例完全匹配:

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    if (mCamera == null || useMe == null) return;

    Camera.Parameters parameters = mCamera.getParameters();
    float previewRatio = (float) useMe.width / useMe.height;
    ViewGroup parentContainer = (ViewGroup) mPreview.getParent();
    int parentWidth = parentContainer.getWidth();
    int parentHeight = parentContainer.getHeight();

    // 计算符合比例的Surface尺寸
    int targetSurfaceWidth, targetSurfaceHeight;
    if ((float) parentWidth / parentHeight > previewRatio) {
        // 容器宽度比预览比例宽,以容器高度为基准计算宽度
        targetSurfaceHeight = parentHeight;
        targetSurfaceWidth = Math.round(targetSurfaceHeight * previewRatio);
    } else {
        // 容器高度比预览比例高,以容器宽度为基准计算高度
        targetSurfaceWidth = parentWidth;
        targetSurfaceHeight = Math.round(targetSurfaceWidth / previewRatio);
    }

    // 设置SurfaceView的布局参数,强制保持比例
    ViewGroup.LayoutParams layoutParams = mPreview.getLayoutParams();
    layoutParams.width = targetSurfaceWidth;
    layoutParams.height = targetSurfaceHeight;
    mPreview.setLayoutParams(layoutParams);

    // 重新配置相机预览尺寸并启动预览
    parameters.setPreviewSize(useMe.width, useMe.height);
    mCamera.setParameters(parameters);
    mCamera.startPreview();
}

3. 修正MediaRecorder的配置冲突

你同时调用了setProfile和setVideoSize,部分设备会因为CamcorderProfile的参数覆盖自定义尺寸,建议调整为:

// 先获取Profile,再修改视频尺寸
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
profile.videoFrameWidth = useMe.width;
profile.videoFrameHeight = useMe.height;
mMediaRecorder.setProfile(profile);
// 这里无需再调用setVideoSize,Profile已包含正确尺寸

总结

只要保证预览控件的宽高比和相机预览/视频录制尺寸的宽高比完全一致,就能解决拉伸变形的问题。上面的步骤从尺寸选择、控件布局到录制配置都做了优化,应该能解决你的问题。

内容的提问来源于stack exchange,提问作者Zee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:16:29