文档目录

一、为什么需要它

从桌面采集(DXGI/PipeWire)拿到的是 BGRA/RGBA 帧,编码器只接受 YUV420P。sws_scale 一次性完成:

  1. 色彩空间转换(RGB → YUV)
  2. 色度下采样(4:4:4 → 4:2:0)
  3. 图像缩放(可选)
struct ColorConverter {
    SwsContext* sws_ctx = nullptr;
    int src_w, src_h, dst_w, dst_h;
    AVPixelFormat src_fmt, dst_fmt;

    bool init(int src_w, int src_h, AVPixelFormat src_fmt,
              int dst_w, int dst_h, AVPixelFormat dst_fmt) {
        this->src_w = src_w; this->src_h = src_h;
        this->dst_w = dst_w; this->dst_h = dst_h;
        this->src_fmt = src_fmt; this->dst_fmt = dst_fmt;

        sws_ctx = sws_getContext(
            src_w, src_h, src_fmt,        // 输入格式
            dst_w, dst_h, dst_fmt,        // 输出格式
            SWS_BILINEAR,                 // 插值算法(性能 vs 质量)
            nullptr, nullptr, nullptr      // 滤波器参数(默认)
        );
        return sws_ctx != nullptr;
    }

    // 把 BGRA 桌面采集帧转为 YUV420P(AVFrame)
    void convert(const uint8_t* bgra_data, int bgra_stride, AVFrame* out_frame) {
        // 封装 sws_scale 需要的数据
        uint8_t* src_data[1] = { (uint8_t*)bgra_data };
        int src_linesize[1] = { bgra_stride };

        sws_scale(sws_ctx,
                  src_data, src_linesize, 0, src_h,
                  out_frame->data, out_frame->linesize);
    }

    ~ColorConverter() {
        sws_freeContext(sws_ctx);
    }
};

// 使用示例:
// ColorConverter conv;
// conv.init(1920, 1080, AV_PIX_FMT_BGRA,   // 桌面采集的格式
//           1920, 1080, AV_PIX_FMT_YUV420P); // 编码器需要的格式
//
// AVFrame* yuv_frame = av_frame_alloc();
// yuv_frame->format = AV_PIX_FMT_YUV420P;
// yuv_frame->width = 1920;
// yuv_frame->height = 1080;
// av_frame_get_buffer(yuv_frame, 32);
//
// conv.convert(bgra_buffer, 1920 * 4, yuv_frame);
// // yuv_frame 现在有 YUV420P 数据,可以送给编码器

二、sws_scale 一步完成的操作对比

如果你手动做:
  BGRA → 逐像素转 YUV444 → UV 下采样到 4:2:0 → 分配 Y/U/V 三个缓冲区
  → 至少 3 次内存操作,3 个循环

sws_scale 一次调用:
  sws_scale(ctx, src, src_linesize, 0, h, dst, dst_linesize);
  内部 SIMD 优化:一次循环完成色彩转换 + 下采样 + 写入三个平面
  → 1 次内存操作,速度是手写的 3-5 倍