如何解决GL_RGB转AVFrame过程中的段错误问题
咱们来一步步拆解你这段RGB转YUV代码触发段错误的原因,以及对应的解决办法:
1. 检查sws_context的初始化状态
你代码里的sws_context如果是全局变量,有没有在使用前初始化为NULL?第一次调用sws_getCachedContext时,如果传入的上下文是NULL,函数会自动创建新的上下文,但如果变量未初始化(是随机垃圾值),就会触发非法内存访问。
- 修复方法:在全局作用域或者函数开头明确初始化:
struct SwsContext *sws_context = NULL;
2. 确认输入RGB尺寸与编码器上下文尺寸完全匹配
你用glReadPixels获取的是DEFAULT_MONITOR.maxResolution对应的宽高像素,但编码器初始化时传入的width和height是不是和这个值一致?如果两者尺寸不匹配,sws_scale会尝试访问超出内存范围的区域,直接触发段错误。
- 修复方法:调用
ffmpeg_encoder_start时,必须传入DEFAULT_MONITOR.maxResolution.width和DEFAULT_MONITOR.maxResolution.height,确保编码器上下文的尺寸和输入RGB数据完全对齐。
3. 处理OpenGL像素数据的行对齐问题
默认情况下,OpenGL会把glReadPixels返回的行数据对齐到4字节边界,这意味着实际行间距(stride)可能不是你硬编码的3 * c->width。比如当宽度是奇数时,3*width是奇数,OpenGL会自动补全到下一个4的倍数,此时你用错误的行间距传入sws_scale,会导致读取到错误的内存位置。
- 修复方法:在调用
glReadPixels前禁用自动对齐:glPixelStorei(GL_PACK_ALIGNMENT, 1); // 强制行数据按1字节对齐,确保行间距为3*width
4. 验证frame指针与内存的有效性
确保frame是通过av_frame_alloc()成功分配,且av_image_alloc()正确初始化了frame->data和frame->linesize。如果frame本身是NULL,或者frame->data[0]等缓冲区指针无效,sws_scale写入数据时会触发段错误。
- 修复方法:在转换函数开头增加有效性检查:
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { if (!c || !frame || !rgb) { std::cerr << "Invalid pointer: codec context, frame or rgb data is null!" << std::endl; return; } // ... 后续转换逻辑 }
5. 确保RGB数据的内存生命周期有效
你用new BYTE[]分配了pixels内存,调用setNextFrame传递指针后,有没有在sws_scale执行完成前就释放了这块内存?如果rgb指针指向的内存已经被释放,访问它就会触发段错误。
- 修复方法:要么确保
pixels内存在转换完成后再释放,要么在setNextFrame内部复制一份RGB数据,而不是直接传递原始指针。
修复后的完整转换函数示例
结合上面的所有要点,修改后的转换函数可以这样写:
// 全局初始化转换上下文为NULL struct SwsContext *sws_context = NULL; static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { // 前置有效性检查 if (!c || !frame || !rgb) { std::cerr << "Invalid input pointers!" << std::endl; return; } const int in_linesize[1] = { 3 * c->width }; // 获取或更新转换上下文,指定缩放算法为SWS_BILINEAR(可选,根据需求调整) sws_context = sws_getCachedContext(sws_context, c->width, c->height, AV_PIX_FMT_RGB24, c->width, c->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); if (!sws_context) { std::cerr << "Failed to create or get sws context!" << std::endl; return; } // 执行格式转换 sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0, c->height, frame->data, frame->linesize); }
内容的提问来源于stack exchange,提问作者benny b

