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

如何用FFmpeg sws_scaler API转换AV_PIX_FMT_VAAPI到YUV420?代码返回NULL求解

解决FFmpeg sws_scaler无法转换VAAPI硬件帧到YUV420P的问题

你遇到的sws_getContext返回NULL的核心原因是:sws_scaler是处理系统内存中像素数据的软件缩放器,它不支持直接与存储在GPU显存中的VAAPI硬件帧(AV_PIX_FMT_VAAPI)交互。要完成转换,你需要先把VAAPI硬件帧下载到系统内存,再用sws_scaler处理。

以下是具体的解决步骤和代码示例:

步骤1:将VAAPI硬件帧下载到系统内存

VAAPI帧存储在GPU显存中,无法直接被sws_scaler读取。你需要使用av_hwframe_transfer_data函数把硬件帧的数据拷贝到系统内存的AVFrame中,选择一个软件支持的像素格式(比如AV_PIX_FMT_NV12,这是VAAPI常用的兼容格式)作为中间格式。

步骤2:用sws_scaler转换为YUV420P

拿到系统内存中的帧后,就可以正常创建sws上下文并执行缩放转换了。

完整示例代码

// 假设你已经有解码后的VAAPI源帧src(格式AV_PIX_FMT_VAAPI)
// 1. 创建用于接收硬件帧下载数据的系统内存帧
AVFrame* hw_download_frame = av_frame_alloc();
if (!hw_download_frame) {
    fprintf(stderr, "Failed to allocate download frame\n");
    return -1;
}
// 设置中间格式为NV12(软件支持),宽高与源帧一致
hw_download_frame->format = AV_PIX_FMT_NV12;
hw_download_frame->width = src->width;
hw_download_frame->height = src->height;

// 为中间帧分配内存
int ret = av_frame_get_buffer(hw_download_frame, 32);
if (ret < 0) {
    fprintf(stderr, "Failed to get buffer for download frame\n");
    av_frame_free(&hw_download_frame);
    return ret;
}

// 将VAAPI硬件帧下载到系统内存
ret = av_hwframe_transfer_data(hw_download_frame, src, 0);
if (ret < 0) {
    fprintf(stderr, "Failed to transfer VAAPI frame to system memory\n");
    av_frame_free(&hw_download_frame);
    return ret;
}

// 2. 创建sws缩放上下文(源格式为NV12,目标为YUV420P)
SwsContext* conversion_context = sws_getContext(
    hw_download_frame->width, hw_download_frame->height, AV_PIX_FMT_NV12,
    scaler_->getWidth(), scaler_->getHeight(), AV_PIX_FMT_YUV420P,
    SWS_BICUBIC, nullptr, nullptr, nullptr
);
if (!conversion_context) {
    fprintf(stderr, "Failed to create sws context\n");
    av_frame_free(&hw_download_frame);
    return -1;
}

// 3. 创建目标YUV420P帧并分配内存
AVFrame* dst_yuv = av_frame_alloc();
if (!dst_yuv) {
    fprintf(stderr, "Failed to allocate target YUV frame\n");
    sws_freeContext(conversion_context);
    av_frame_free(&hw_download_frame);
    return -1;
}
dst_yuv->format = AV_PIX_FMT_YUV420P;
dst_yuv->width = scaler_->getWidth();
dst_yuv->height = scaler_->getHeight();

ret = av_frame_get_buffer(dst_yuv, 32);
if (ret < 0) {
    fprintf(stderr, "Failed to get buffer for target YUV frame\n");
    av_frame_free(&dst_yuv);
    sws_freeContext(conversion_context);
    av_frame_free(&hw_download_frame);
    return ret;
}

// 4. 执行缩放转换
sws_scale(
    conversion_context,
    (const uint8_t* const*)hw_download_frame->data, hw_download_frame->linesize,
    0, hw_download_frame->height,
    dst_yuv->data, dst_yuv->linesize
);

// 5. 释放资源(必须做,避免内存泄漏)
sws_freeContext(conversion_context);
av_frame_free(&hw_download_frame);
// 注意:dst_yuv使用完后也要记得释放
// av_frame_free(&dst_yuv);

额外注意事项

  • 确保你的FFmpeg编译时启用了VAAPI支持(编译参数加--enable-vaapi),否则av_hwframe_transfer_data等函数会不可用。
  • 源VAAPI帧的hw_frames_ctx必须正确初始化(这通常在解码前的硬件上下文配置阶段完成),否则帧传输会失败。
  • 如果你不需要缩放,只是想把VAAPI帧转成YUV420P,可以直接把中间帧的格式设为AV_PIX_FMT_YUV420P,跳过sws_scaler的步骤,这样效率更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:28:43