liudianwu |
2023-01-06 14:59 |
Qt音视频开发10-ffmpeg内核硬解码
## 一、前言 为了极大的降低CPU的占用,实现硬解码(也叫硬件加速)非常有必要,一个视频文件或者一路视频流还好,如果增加到64路视频流呢,如果是4K、8K这种高分辨率的视频呢,必须安装上硬解码才是上上策。举个例子在电脑上播放4K以上的H265这类的视频文件,如果不开硬解码,很容易出现卡顿现象,在配置高的电脑也容易出现,毕竟非常的耗CPU资源,来不及刷新,上了硬解码之后,明显流畅的不要不要的,怪不得现在的显卡性能越做越牛逼,就是为了在显示这块尽可能的分担CPU的压力,以便留出CPU时间片做其他的事情。
关于ffmpeg解码,网上搜索到的代码绝对是一大堆一大堆,而且很多都讲得很详细,解码的函数流程图非常清晰,关于ffmpeg这块的学习本人推荐雷神的博客,分析的相当细致,我在很久以前刚用Qt+ffmpeg解码的时候,参考的就是雷神的例子,当然这些demo其实在ffmpeg的开发包dev下的examples也是非常详细的,只不过没有什么分析过程,参考雷神的博客可以看到很多分析过程。
硬解码大致流程:
01. 根据硬解码类型查找硬解码设备类型 av_hwdevice_find_type_by_name 02. 根据解码设备类型找到硬解码的格式 find_fmt_by_hw_type 03. 获取解码器格式回调 videoCodecCtx->get_format 04. 创建硬解码设备 av_hwdevice_ctx_create 05. 设备硬解码设备 videoCodecCtx->hw_device_ctx 06. 解码数据包 avcodec_send_packet avcodec_receive_frame 07. 将硬解码后的数据从GPU转换取出来 av_hwframe_transfer_data
## 二、效果图 [attachment=23233]
## 三、体验地址 1. 国内站点:[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun) 2. 国际站点:[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun) 3. 个人作品:[https://blog.csdn.net/feiyangqingyun/article/details/97565652](https://blog.csdn.net/feiyangqingyun/article/details/97565652) 4. 体验地址:[https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g](https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g) 提取码:01jf 文件名:bin_video_demo/bin_linux_video。
## 四、相关代码 ```cpp bool FFmpegThread::initHardware() { if (hardware == "none") { return true; } #if (FFMPEG_VERSION_MAJOR > 2) //根据名称自动寻找硬解码 enum AVHWDeviceType type; //发现嵌入式上低版本的库没有av_hwdevice_find_type_by_name函数 #ifdef __arm__ #if (FFMPEG_VERSION_MAJOR < 4) return false; #else type = av_hwdevice_find_type_by_name(hardware.toUtf8().data()); #endif #else type = av_hwdevice_find_type_by_name(hardware.toUtf8().data()); #endif debug("硬件加速", QString("名称: %1 数值: %2").arg(hardware).arg(type));
//找到对应的硬解码格式 FFmpegHelper::hw_pix_fmt = FFmpegHelper::find_fmt_by_hw_type(type); if (FFmpegHelper::hw_pix_fmt == -1) { debug("加速失败", "错误: 未找到对应加速类型"); return false; }
int result = -1; //解码器格式赋值为硬解码 videoCodecCtx->get_format = FFmpegHelper::get_hw_format; //av_opt_set_int(videoCodecCtx, "refcounted_frames", 1, 0);
//创建硬解码设备 AVBufferRef *hw_device_ref; result = av_hwdevice_ctx_create(&hw_device_ref, type, NULL, NULL, 0); if (result < 0) { debug("加速失败", "错误: 创建视频解码器失败 " + FFmpegHelper::getError(result)); return false; }
videoCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ref); av_buffer_unref(&hw_device_ref); return true; #else return false; #endif }
int FFmpegHelper::decode(FFmpegThread *thread, AVCodecContext *avctx, AVPacket *packet, AVFrame *frameSrc, AVFrame *frameDst) { int result = -1; #ifdef videoffmpeg QString flag = "硬解出错"; #if (FFMPEG_VERSION_MAJOR > 2) result = avcodec_send_packet(avctx, packet); if (result < 0) { thread->debug(flag, QString("步骤: %1 原因: %2").arg("avcodec_send_packet").arg(getError(result))); return result; }
while (result >= 0) { result = avcodec_receive_frame(avctx, frameSrc); if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) { break; } else if (result < 0) { thread->debug(flag, QString("步骤: %1 原因: %2").arg("avcodec_receive_frame").arg(getError(result))); break; }
//将数据从GPU拷贝到CPU result = av_hwframe_transfer_data(frameDst, frameSrc, 0); if (result < 0) { av_frame_unref(frameDst); av_frame_unref(frameSrc); thread->debug(flag, QString("步骤: %1 原因: %2").arg("av_hwframe_transfer_data").arg(getError(result))); return result; } goto end; } #endif return result;
end: //调用线程处理解码后的数据 thread->decodeVideo2(packet); #endif return result; } ```
|
|