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

Android NDK中YUV420SP转RGB显示后视频无法适配SurfaceView

解决NDK手动渲染视频帧适配SurfaceView缩放问题

嘿,针对你遇到的视频无法缩放适配SurfaceView的问题,我整理了几个实用的解决思路,都是基于你当前的NDK手动渲染逻辑来的:

1. 先获取窗口实际尺寸,计算合理的缩放比例

首先你需要拿到SurfaceView对应的Native窗口的实际宽高,再和视频帧的宽高对比,计算出保持比例的缩放值,避免拉伸变形:

// 获取Native窗口的实际显示尺寸
int windowWidth = ANativeWindow_getWidth(mWindow);
int windowHeight = ANativeWindow_getHeight(mWindow);

// 计算视频帧与窗口的宽高比,选择适配模式(这里以"适配窗口不超出"为例)
float scaleX = static_cast<float>(windowWidth) / w;
float scaleY = static_cast<float>(windowHeight) / h;
float scale = std::min(scaleX, scaleY); // 若要填充整个窗口,改用std::max

// 计算缩放后视频在窗口中的显示区域(居中显示)
int dstWidth = static_cast<int>(w * scale);
int dstHeight = static_cast<int>(h * scale);
int dstStartX = (windowWidth - dstWidth) / 2;
int dstStartY = (windowHeight - dstHeight) / 2;

2. 修改渲染逻辑,实现缩放绘制

原来的代码是1:1逐像素映射,现在需要根据缩放比例,对视频帧的像素进行采样后再绘制到窗口的对应位置。这里提供两种简单的采样方式:

方式一:最近邻插值(性能高,画质一般)

这种方式直接取最接近的源像素,适合对性能要求高的场景:

int* dstLine = reinterpret_cast<int*>(buffer.bits);
int const frameSize = w * h;

for (int dy = 0; dy < dstHeight; ++dy) {
    // 计算当前目标行对应的源视频行
    int srcY = static_cast<int>(dy / scale);
    for (int dx = 0; dx < dstWidth; ++dx) {
        // 计算当前目标列对应的源视频列
        int srcX = static_cast<int>(dx / scale);

        // 读取YUV420SP的像素值
        int indexY = srcY * w + srcX;
        int indexU = frameSize + (srcY / 2) * w + (srcX / 2) * 2;
        int indexV = indexU + 1;

        int Y = 0xFF & decodedBuff[indexY];
        int U = 0xFF & decodedBuff[indexU];
        int V = 0xFF & decodedBuff[indexV];

        // YUV转RGB(保留你原来的转换逻辑)
        int R = static_cast<int>(Y + 1.402f * (V - 128));
        int G = static_cast<int>(Y - 0.344f * (U - 128) - 0.714f * (V - 128));
        int B = static_cast<int>(Y + 1.772f * (U - 128));

        // 颜色值 clamping
        R = std::clamp(R, 0, 255);
        G = std::clamp(G, 0, 255);
        B = std::clamp(B, 0, 255);

        // 绘制到窗口的目标位置(加上偏移量)
        dstLine[buffer.stride * (dstStartY + dy) + (dstStartX + dx)] = 0xff000000 + (B << 16) + (G << 8) + R;
    }
}

方式二:双线性插值(画质更平滑,性能稍低)

如果追求更好的画质,可以用双线性插值,对周围四个源像素的颜色加权平均,代码逻辑会稍复杂,但显示效果更细腻,适合高清视频场景。

3. 调整Native窗口的缓冲区设置

你当前设置的ANativeWindow_setBuffersGeometry参数是视频帧的宽高,建议改成Native窗口的实际尺寸,让缓冲区和SurfaceView大小一致,这样渲染时更贴合窗口:

int windowWidth = ANativeWindow_getWidth(mWindow);
int windowHeight = ANativeWindow_getHeight(mWindow);
ANativeWindow_setBuffersGeometry(mWindow, windowWidth, windowHeight, WINDOW_FORMAT_RGBA_8888);

4. 进阶方案:用OpenGL ES硬件加速渲染

如果手动软件缩放的性能跟不上(比如高分辨率视频),可以考虑在Native层引入OpenGL ES,把YUV帧作为纹理上传,通过GLSL shader完成颜色转换和缩放,利用GPU加速。这种方式不仅缩放更高效,还能支持更复杂的渲染效果,不过需要额外编写EGL和OpenGL的初始化、纹理渲染代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:25:39