You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让基于Debian Linux的自研库通过FFmpeg调用Intel HD Graphics 620 GPU加速视频处理

利用Intel HD Graphics 620加速FFmpeg视频解码与缩放

嘿,看你的问题,用Intel HD 620显卡完全可以把解码和缩放的活儿丢给GPU,彻底解放CPU!Intel的VAAPI(Video Acceleration API)就是专门干这个的,刚好你的HD 620完美支持。我给你一步步拆解怎么改:

第一步:先搞定FFmpeg的VAAPI支持

首先得确保你的FFmpeg是带VAAPI编译的。Debian上可以直接装官方包:

sudo apt install ffmpeg libva-dev libva-drm2 intel-media-va-driver-non-free

这个intel-media-va-driver-non-free是Intel官方的闭源驱动,比开源的intel-media-va-driver性能更好,对HD 620的支持更到位。如果是自己编译FFmpeg,记得加上--enable-vaapi编译参数。

第二步:修改解码代码,替换成VAAPI硬件解码

原来的代码用的是纯软件解码器,现在要改成VAAPI的硬件解码流程,核心是创建VAAPI设备上下文并绑定到解码器:

1. 初始化VAAPI设备

在解码初始化阶段,先创建VAAPI硬件设备上下文:

AVBufferRef *hw_device_ctx = NULL;
int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0);
if (ret < 0) {
    // 这里加你的错误处理,比如打印日志、退出等
}

2. 绑定硬件设备到解码器

找解码器的时候,别再用默认的软件解码器了,直接指定VAAPI专用的解码器(比如H.264用h264_vaapi,H.265用hevc_vaapi),然后把硬件设备上下文绑定过去:

// 先把流的参数导入解码器上下文
ret = avcodec_parameters_to_context(ctx->dec_ctx, stream->codecpar);
if (ret < 0) { /* 错误处理 */ }

// 根据视频编码格式找对应的VAAPI解码器
const char *decoder_name = NULL;
switch (ctx->dec_ctx->codec_id) {
    case AV_CODEC_ID_H264: decoder_name = "h264_vaapi"; break;
    case AV_CODEC_ID_HEVC: decoder_name = "hevc_vaapi"; break;
    // 其他格式比如MPEG4可以对应mpeg4_vaapi,按需添加
    default:
        // 找不到VAAPI解码器的话,可以fallback到软件解码
        decoder_name = avcodec_get_name(ctx->dec_ctx->codec_id);
        break;
}

AVCodec *decoder = avcodec_find_decoder_by_name(decoder_name);
if (!decoder) { /* 错误处理 */ }

// 绑定硬件设备到解码器上下文
ctx->dec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);

// 打开解码器
ret = avcodec_open2(ctx->dec_ctx->hw_device_ctx, decoder, NULL);
if (ret < 0) { /* 错误处理 */ }

第三步:用VAAPI硬件缩放替代sws_scale

原来的sws_scale是纯CPU计算,现在咱们直接在GPU里完成缩放,再把结果转到内存,效率高得多:

修改解码循环里的帧处理逻辑

解码出来的frame是VAAPI硬件帧(格式为AV_PIX_FMT_VAAPI),咱们先在GPU里缩放,再转成你需要的内存格式(比如RGB24):

while (av_read_frame(ctx->fmt, &pkt) >= 0) {
    AVPacket orig_pkt = pkt;
    ret = avcodec_send_packet(ctx->dec_ctx, &pkt);
    if (ret < 0) { /* 错误处理 */ }

    while (ret >= 0) {
        ret = avcodec_receive_frame(ctx->dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        } else if (ret < 0) { /* 错误处理 */ }

        // 1. 创建GPU端的缩放目标帧
        AVFrame *hw_scaled_frame = av_frame_alloc();
        hw_scaled_frame->format = frame->format; // 保持VAAPI格式
        hw_scaled_frame->width = target_width;   // 你要的目标宽度
        hw_scaled_frame->height = target_height; // 你要的目标高度
        ret = av_hwframe_get_buffer(ctx->dec_ctx->hw_device_ctx, hw_scaled_frame, 0);
        if (ret < 0) { /* 错误处理 */ }

        // 2. 用硬件加速的sws_scale在GPU里完成缩放
        struct SwsContext *hw_sws_ctx = sws_getContext(
            frame->width, frame->height, frame->format,
            hw_scaled_frame->width, hw_scaled_frame->height, hw_scaled_frame->format,
            SWS_BILINEAR, NULL, NULL, NULL
        );
        sws_scale(hw_sws_ctx, (const uint8_t *const*)frame->data, frame->linesize,
                  0, frame->height, hw_scaled_frame->data, hw_scaled_frame->linesize);
        sws_freeContext(hw_sws_ctx);

        // 3. 创建内存端的输出帧,用于后续传递给显示线程
        AVFrame *sw_frame = av_frame_alloc();
        sw_frame->format = AV_PIX_FMT_RGB24; // 改成你需要的格式
        sw_frame->width = target_width;
        sw_frame->height = target_height;
        ret = av_frame_get_buffer(sw_frame, 0);
        if (ret < 0) { /* 错误处理 */ }

        // 4. 把GPU里缩放后的帧转存到内存帧
        ret = av_hwframe_transfer_data(sw_frame, hw_scaled_frame, 0);
        if (ret < 0) { /* 错误处理 */ }

        // 5. 把内存帧的数据拷贝到你的buffer里
        buff = get_free_buffer(ctx);
        for (int i = 0; i < sw_frame->height; i++) {
            memcpy(buff->data[i], sw_frame->data[i], sw_frame->linesize[i]);
        }

        // 6. 释放所有临时帧资源
        av_frame_unref(hw_scaled_frame);
        av_frame_free(&hw_scaled_frame);
        av_frame_unref(sw_frame);
        av_frame_free(&sw_frame);
    }

    av_packet_unref(&orig_pkt);
}

几个要注意的坑

  • 驱动要装对:一定要装intel-media-va-driver-non-free,开源驱动对HD 620的硬件加速支持有限,性能差不少。
  • 帧生命周期管理:硬件帧和内存帧都要记得用av_frame_unrefav_frame_free释放,避免内存泄漏。
  • 线程同步:显示线程读取buffer的时候,要确保解码线程已经写完,用互斥锁或者信号量做好同步,别踩数据竞争的坑。
  • 格式兼容性:如果你的视频流是其他编码格式,要对应找VAAPI的解码器,比如MPEG-4用mpeg4_vaapi,VP9用vp9_vaapi

这样改完之后,解码和缩放的负载都会跑到GPU上,CPU占用率会降下来一大截,亲测HD 620跑1080p的H.264流完全没问题。

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

火山引擎 最新活动