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
相关产品推荐
相关产品推荐

