如何借助Intel QuickSync实现FFmpeg中NV12转YUV420的硬件加速?
可以通过Intel QuickSync实现硬件加速的色彩转换
你当前使用的sws_scale是纯CPU处理,完全没有用到QuickSync加速。通过全程保留硬件帧、利用QSV的硬件转换能力,能避免将帧数据从GPU传到CPU的开销,实现格式转换的硬件加速。
核心思路
不要提前调用av_hwframe_transfer_data将QSV帧传到CPU,直接在硬件域内完成格式转换,最后按需将转换后的帧传到CPU内存(如果需要)。
实现方案一:基于硬件帧直接转换
// 解码得到QSV硬件帧 _tmpFrame (格式为AV_PIX_FMT_QSV) // 提前初始化硬件帧上下文(仅需执行一次) static AVBufferRef *hw_frames_ref = NULL; if (!hw_frames_ref) { hw_frames_ref = av_hwframe_ctx_alloc(_tmpFrame->hw_frames_ctx->device_ref); AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)hw_frames_ref->data; hw_frames_ctx->format = AV_PIX_FMT_QSV; hw_frames_ctx->sw_format = AV_PIX_FMT_YUV420P; // 目标输出格式 hw_frames_ctx->width = _context->width; hw_frames_ctx->height = _context->height; av_hwframe_ctx_init(hw_frames_ref); } // 1. 创建硬件目标帧 AVFrame *hw_dst_frame = av_frame_alloc(); hw_dst_frame->hw_frames_ctx = av_buffer_ref(hw_frames_ref); av_hwframe_get_buffer(hw_dst_frame->hw_frames_ctx, hw_dst_frame, 0); // 2. 硬件内格式转换(QuickSync加速) int32_t ret = av_hwframe_transfer_data(hw_dst_frame, _tmpFrame, 0); if (ret < 0) { // 错误处理逻辑 } // 3. (可选)将转换后的硬件帧传到CPU内存,得到YUV420P格式的帧 AVFrame *dst_frame = av_frame_alloc(); dst_frame->format = AV_PIX_FMT_YUV420P; dst_frame->width = _context->width; dst_frame->height = _context->height; av_frame_get_buffer(dst_frame, 32); ret = av_hwframe_transfer_data(dst_frame, hw_dst_frame, 0); if (ret < 0) { // 错误处理逻辑 } // 此时dst_frame即为硬件加速转换后的YUV420P帧,可直接使用 // 资源释放 av_frame_free(&hw_dst_frame); av_frame_free(&dst_frame);
实现方案二:使用QSV滤镜链
如果需要更灵活的处理(比如同时做缩放、格式转换),可以用FFmpeg的QSV滤镜实现全硬件加速的处理链:
// 提前初始化滤镜图(仅需执行一次) static AVFilterGraph *filter_graph = NULL; static AVFilterContext *buffer_src_ctx = NULL; static AVFilterContext *buffer_sink_ctx = NULL; if (!filter_graph) { filter_graph = avfilter_graph_alloc(); // 输入为QSV硬件帧,输出为YUV420P const char *filter_desc = "format_qsv=format=yuv420p"; // 初始化输入滤镜 AVFilter *buffer_src = avfilter_get_by_name("buffer"); AVDictionary *src_args = NULL; av_dict_set(&src_args, "video_size", av_asprintf("%dx%d", _context->width, _context->height), 0); av_dict_set(&src_args, "pix_fmt", av_get_pix_fmt_name(AV_PIX_FMT_QSV), 0); av_dict_set(&src_args, "hw_frames_ctx", av_buffer_ref(_tmpFrame->hw_frames_ctx), 0); avfilter_graph_create_filter(&buffer_src_ctx, buffer_src, "src", NULL, &src_args, filter_graph); // 初始化输出滤镜 AVFilter *buffer_sink = avfilter_get_by_name("buffersink"); AVDictionary *sink_args = NULL; av_dict_set(&sink_args, "pix_fmts", av_get_pix_fmt_name(AV_PIX_FMT_YUV420P), 0); avfilter_graph_create_filter(&buffer_sink_ctx, buffer_sink, "sink", NULL, &sink_args, filter_graph); // 链接滤镜并配置 avfilter_link(buffer_src_ctx, 0, buffer_sink_ctx, 0); avfilter_graph_config(filter_graph, NULL); } // 处理单帧 av_buffersrc_add_frame(buffer_src_ctx, _tmpFrame); AVFrame *dst_frame = av_frame_alloc(); av_buffersink_get_frame(buffer_sink_ctx, dst_frame); // 此时dst_frame就是硬件加速转换后的YUV420P帧 // 资源释放 av_frame_free(&dst_frame);
关键注意事项
- 确保FFmpeg编译时开启了QSV支持:编译参数需添加
--enable-libmfx --enable-qsv。 - 硬件上下文
hw_frames_ctx需提前初始化,避免重复创建带来的性能开销。 - 全程保留硬件帧处理,避免不必要的GPU-CPU数据拷贝,这是提升性能的核心。
内容的提问来源于stack exchange,提问作者zerpico
相关产品推荐
相关产品推荐

