如何让基于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_unref和av_frame_free释放,避免内存泄漏。 - 线程同步:显示线程读取buffer的时候,要确保解码线程已经写完,用互斥锁或者信号量做好同步,别踩数据竞争的坑。
- 格式兼容性:如果你的视频流是其他编码格式,要对应找VAAPI的解码器,比如MPEG-4用
mpeg4_vaapi,VP9用vp9_vaapi。
这样改完之后,解码和缩放的负载都会跑到GPU上,CPU占用率会降下来一大截,亲测HD 620跑1080p的H.264流完全没问题。
内容的提问来源于stack exchange,提问作者zimopisec




