如何使用WPFMediaKit无故障加载播放视频,解决内存泄漏、加载失败问题
WPFMediaKit媒体播放内存泄漏与加载失败修复方案
根因定位
- 核心问题为DirectShow非托管资源泄漏:WPFMediaKit底层封装的是DirectShow COM组件,属于非托管资源,现有释放逻辑仅调用
Close()方法,没有主动销毁COM对象和视频帧缓存。你使用的5000×400分辨率视频单帧RGB缓存接近8MB,频繁加载但不彻底释放的情况下,1-2小时即可占满可用内存,后续DirectShow实例创建失败,MediaOpened事件自然无法触发。 - WPF UI元素引用残留:你添加到ListBox的
Video_playing属于UI元素,若被WPF视觉树、事件系统隐性持有引用,会导致GC无法回收托管实例,进一步阻碍非托管资源释放。 - 异常场景未处理:偶发加载失败的视频实例没有对应的资源清理逻辑,会形成僵尸实例持续占用内存。
修复步骤
1. 完善资源释放逻辑
在原有清理逻辑基础上增加非托管资源销毁、GC强制回收步骤:
if (mediasListBoxInfo[i][1].Equals("media")) { var video = (Video_playing)mediasListBox.Items[i]; // 先暂停播放,避免资源锁导致释放失败 video.Pause(); // 解绑所有事件 video.MediaOpened -= Video_MediaOpened; // 如果有绑定MediaFailed事件也需要解绑 // video.MediaFailed -= Video_MediaFailed; video.UnloadedBehavior = WPFMediaKit.DirectShow.MediaPlayers.MediaState.Close; video.Close(); // 新增:调用IDisposable接口释放DirectShow COM资源 if (video is IDisposable disposable) { disposable.Dispose(); } // 手动置空引用 video = null; } mediasListBoxInfo.RemoveAt(i); mediasListBox.Items.RemoveAt(i); // 批量清理资源后触发一次全量GC回收,不用每次移除都调用 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
2. 优化MediaOpened事件逻辑
原有遍历ListBox的逻辑效率低且容易产生意外引用,直接转换sender即可:
private void Video_MediaOpened(object sender, System.Windows.RoutedEventArgs e) { if(enablePause && sender is Video_playing video) { video.Pause(); } }
3. 新增加载失败异常处理
绑定MediaFailed事件,加载失败时直接清理对应实例,避免内存占用:
// 视频初始化时绑定失败事件 Video_playing video = new Video_playing(fname); video.MediaFailed += Video_MediaFailed; // 事件实现 private void Video_MediaFailed(object sender, System.Windows.ExceptionRoutedEventArgs e) { // 此处调用你的资源清理逻辑,直接销毁加载失败的实例 }
4. 可选优化
如果轮播的视频重复率较高,可实现视频实例缓存池,无需每次都销毁重建实例,既降低DirectShow组件创建销毁的开销,也能减少资源泄漏概率。
内容的提问来源于stack exchange,提问作者V. Andris
相关产品推荐
相关产品推荐

