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

如何修正从Cairo表面向FFmpeg AVFrame填充数据时的RGB颜色异常问题

问题原因及修复方案

核心错误点

你当前的代码存在3个直接导致颜色异常的问题:

  • Cairo图像表面默认格式为CAIRO_FORMAT_ARGB32,小端模式下每个4字节像素的存储顺序为 B(偏移0)、G(偏移1)、R(偏移2)、A(偏移3),你直接将B值赋值给Y通道、G值赋值给U通道,还注释掉了R值对应的V通道赋值,红色分量完全丢失,自然红色图形显示异常
  • 如果你使用的是FFmpeg默认的YUV420P格式AVFrame,U、V通道的宽高均为Y通道的1/2,你逐像素填充U、V通道会直接造成内存越界,还会导致采样完全错误
  • 没有做RGB到YUV的色彩空间转换,直接把RGB值硬塞到YUV通道,本身就会产生严重的色偏

修复方案

方案1:使用匹配的像素格式(推荐,性能最优)

直接将AVFrame的像素格式设置为和Cairo surface匹配的AV_PIX_FMT_BGRA,无需色彩转换,直接按行拷贝即可,绝大多数主流视频编码器都支持该输入格式:

/* Image info and pixel data */
int width  = cairo_image_surface_get_width( surface );
int height = cairo_image_surface_get_height( surface );
int stride = cairo_image_surface_get_stride( surface );
uint8_t* pix    = cairo_image_surface_get_data( surface );

// 分配AVFrame时指定格式为AV_PIX_FMT_BGRA,宽高和surface一致
for( int row = 0; row < height; row++ )
{
    // 直接按行拷贝,不需要逐像素操作,效率更高
    memcpy(
        img->video_frame->data[0] + row * img->video_frame->linesize[0],
        pix + row * stride,
        width * 4 // 每个像素占4字节
    );
}
img->video_frame->pts++;

方案2:转换为YUV420P格式(适配要求YUV输入的场景)

如果你的编码器强制要求YUV420P输入,使用FFmpeg内置的sws_scale工具做色彩空间转换,不需要手动写转换公式避免踩坑:

  1. 初始化转换上下文(全局初始化一次即可,不要每次编码都重复创建)
struct SwsContext* sws_ctx = sws_getContext(
    width, height, AV_PIX_FMT_BGRA, // 输入格式:Cairo的BGRA
    width, height, AV_PIX_FMT_YUV420P, // 输出格式:YUV420P
    SWS_BILINEAR, NULL, NULL, NULL
);
  1. 每次编码前做格式转换
uint8_t* src_data[4] = {pix, NULL, NULL, NULL};
int src_linesize[4] = {stride, 0, 0, 0};
sws_scale(sws_ctx, src_data, src_linesize, 0, height,
          img->video_frame->data, img->video_frame->linesize);
  1. 程序退出前释放上下文
sws_freeContext(sws_ctx);

额外注意事项

  • 如果确认你的Cairo surface使用的是CAIRO_FORMAT_RGB24,对应FFmpeg像素格式改为AV_PIX_FMT_BGR24即可
  • 若转换后仍有轻微色偏,检查AVFrame的color_range参数,根据需要设置为全范围(AVCOL_RANGE_JPEG,0255)或有限范围(`AVCOL_RANGE_MPEG`,16235)即可修复

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 18:15:00