SDL_LockTexture返回异常pitch值求助:Android原生相机预览场景
问题分析与解决方案
你的假设确实存在偏差,而且对SDL处理NV21格式纹理的内存布局和pitch的定义理解有误,同时数据拷贝逻辑也需要调整。下面一步步拆解问题:
1. SDL与Android对NV21格式的pitch定义差异
你预期的720*12/8=1080是基于Android ImageFormat.getBitsPerPixel(NV21)的总位深计算的整个帧的平均行字节数,但SDL中SDL_LockTexture返回的pitch对于半平面YUV格式有完全不同的含义:
- SDL的
SDL_PIXELFORMAT_NV21属于半平面格式,内存布局是:先存完整的Y分量平面(每个Y像素占1字节,共720*480字节),之后紧跟VU交错的UV分量平面(每2x2像素共享一组VU,共720*480/2字节)。 - 此时
SDL_LockTexture返回的pitch是Y分量平面的行字节数,也就是你的宽度值720(因为每个Y像素1字节,每行Y刚好是720字节),而非整个帧的行字节数。
2. 画面偏绿的核心原因
当你使用SDL返回的pitch=720时,大概率是你的数据拷贝逻辑出了问题:
- 如果你循环480次(对应画面高度),每次拷贝
pitch字节,那你只拷贝了Y分量的全部数据,UV分量完全没被写入纹理内存。缺失UV分量会导致色彩异常,偏绿是这类问题的典型表现。 - 而当你手动用
1080作为pitch时,480*1080刚好等于NV21帧的总字节数(720*480*3/2),相当于完整拷贝了整个NV21数据到纹理内存,巧合匹配了SDL的半平面布局,所以画面显示正常。但这不是正确的用法,你只是歪打正着绕过了pitch的误解。
3. 正确的处理方式
针对SDL的NV21纹理,推荐两种正确的数据拷贝方式:
方式一:分平面拷贝(更贴合SDL的内存布局)
// 锁定纹理 void* pixels; int pitch; SDL_LockTexture(texture, nullptr, &pixels, &pitch); // 假设camera_data是Android相机输出的NV21数据指针 size_t y_plane_size = 720 * 480; size_t uv_plane_size = 720 * 480 / 2; // 拷贝Y分量:每行pitch字节,共480行 memcpy(pixels, camera_data, y_plane_size); // 拷贝UV分量:UV平面起始地址是pixels + Y平面大小,每行同样是pitch字节,共240行 memcpy((uint8_t*)pixels + y_plane_size, camera_data + y_plane_size, uv_plane_size); // 解锁纹理 SDL_UnlockTexture(texture);
方式二:直接完整拷贝(更简洁)
因为总字节数完全匹配,你可以跳过pitch的逐行处理,直接拷贝整个NV21数据:
SDL_LockTexture(texture, nullptr, &pixels, &pitch); memcpy(pixels, camera_data, 720 * 480 * 3 / 2); SDL_UnlockTexture(texture);
额外提示
你使用的SDL2.0.7版本相对老旧,Android平台的NV21格式支持可能存在细节瑕疵,如果后续遇到其他异常,可以尝试升级到SDL2.26+的新版本。另外,Android相机输出的NV21布局和SDL的SDL_PIXELFORMAT_NV21完全一致(Y在前、VU交错在后),不需要调整UV顺序。
内容的提问来源于stack exchange,提问作者Paolo M
相关产品推荐
相关产品推荐

