FFmpeg.AutoGen C# GPU解码10路以上视频出现异常问题求助
问题成因
- 硬件限制:AMD Radeon R7 430是入门级显卡,D3D11VA硬解会话上限本身就只有10路左右,超过上限就会触发解码器创建失败错误。
- 代码资源浪费:现有代码每一路解码线程都单独创建独立的硬件设备上下文,重复占用显卡资源,进一步降低了可支持的并发路数。
- 资源泄漏:
VideoStreamDecoder的Dispose方法没有释放硬件设备上下文的引用,长时间运行会导致显卡资源泄漏,加重资源不足的问题。 - 代码逻辑漏洞:Main函数中存在C#经典闭包问题,循环变量直接传入线程委托,可能导致多个线程读取到同一个错误的文件路径。
- 无容错处理:硬件解码器创建失败后没有降级逻辑,代码继续访问空指针直接触发内存访问异常。
修复方案
1. 共享全局硬件设备上下文
整个程序只初始化一次硬件设备上下文,所有解码线程共享该上下文,仅通过增加引用计数的方式关联到每个解码上下文,避免重复创建资源。
2. 补全资源释放逻辑
在解码器销毁时主动释放硬件上下文的引用,避免资源泄漏。
3. 修复闭包问题
循环内部声明临时变量存储每个线程的视频路径,避免多线程读取变量冲突。
4. 增加降级容错逻辑
硬解初始化失败时自动切换为CPU软解,避免程序直接崩溃。
核心修改代码示例
全局硬件上下文初始化
// 全局共享硬件设备缓冲区 private static AVBufferRef* _hwDeviceCtx = null; // 程序启动时仅调用一次 private static unsafe void InitHardwareDevice(AVHWDeviceType type) { ffmpeg.av_hwdevice_ctx_create(&_hwDeviceCtx, type, null, null, 0).ThrowExceptionIfError(); }
修改VideoStreamDecoder构造函数的硬件关联逻辑
if (HWDeviceType != AVHWDeviceType.AV_HWDEVICE_TYPE_NONE) { // 复用全局硬件上下文,仅增加引用计数 _pCodecContext->hw_device_ctx = ffmpeg.av_buffer_ref(_hwDeviceCtx); if(_pCodecContext->hw_device_ctx == null) { // 引用失败自动降级为软解 HWDeviceType = AVHWDeviceType.AV_HWDEVICE_TYPE_NONE; } }
补全Dispose方法的资源释放
public void Dispose() { ffmpeg.av_frame_unref(_pFrame); ffmpeg.av_free(_pFrame); ffmpeg.av_frame_unref(_receivedFrame); ffmpeg.av_free(_receivedFrame); ffmpeg.av_packet_unref(_pPacket); ffmpeg.av_free(_pPacket); // 新增:释放硬件上下文引用 if (_pCodecContext->hw_device_ctx != null) { ffmpeg.av_buffer_unref(&_pCodecContext->hw_device_ctx); } ffmpeg.avcodec_close(_pCodecContext); var pFormatContext = _pFormatContext; ffmpeg.avformat_close_input(&pFormatContext); }
修复Main函数的闭包和容错逻辑
private static void Main(string[] args) { // 先初始化全局硬件设备 InitHardwareDevice(AVHWDeviceType.AV_HWDEVICE_TYPE_D3D11VA); for (int i = 0; i < 11; i++) { // 循环内声明临时变量,避免闭包问题 string threadUrl = @"D:\video\abc" + i + ".mp4"; new Thread(() => { try { DecodeAllFramesToImages(AVHWDeviceType.AV_HWDEVICE_TYPE_D3D11VA, threadUrl); } catch(Exception ex) { Console.WriteLine($"硬解{threadUrl}失败,切换软解:{ex.Message}"); DecodeAllFramesToImages(AVHWDeviceType.AV_HWDEVICE_TYPE_NONE, threadUrl); } }).Start(); } // 程序退出时释放全局硬件上下文 ffmpeg.av_buffer_unref(&_hwDeviceCtx); }
内容的提问来源于stack exchange,提问作者Sang Nguyen
相关产品推荐
相关产品推荐

