WPF MediaElement视频冻结

本文关键字:冻结 视频 MediaElement WPF | 更新日期: 2023-09-27 17:58:16

我在wpf项目中使用Image和MediaElement,在这里我显示文件系统中的图像和视频。我有几个定时器,可以将文件加载到Image/MediaElement控件。一切正常工作4-5个小时,但随后MediaElement视频文件冻结,MediaEnded事件不会发生。我重新启动了应用程序,它运行时没有任何问题,但几个小时后,这个问题再次出现。

我的WPF XAML代码:

<Grid Name="MainGrid">
    <Image HorizontalAlignment="Center" VerticalAlignment="Center" Name="MainImage" Stretch="Fill" />
    <MediaElement MediaEnded="MediaEnded" MediaOpened="MediaOpened" LoadedBehavior="Manual" HorizontalAlignment="Center" Name="VideoControl" VerticalAlignment="Center"  
                   Stretch="Fill" UnloadedBehavior="Manual"/>
</Grid>

C#代码:

public partial class ImageView
{
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();
    private static String _advCheckGuid;
    private List<String> _FolderNames;
    private int _FolderIndex = 0;
    private MainWindow _MainWindow;
    private List<String> _PathList;
    private List<String> _CheckPathList; 
    private int _Index;
    private BitmapImage _BitmapImage;
    private volatile bool _Running = true;
    private Backend _Backend;
    private ApplicationDeployment _UpdateCheck;
    // Threads
    private Timer _ImageTimer;
    private Timer _UpdateTimer;
    private Timer _FolderClearTimer;
    private Timer _CheckApplicationUpdateTimer;
    private Thread _TerminationThread;

    public ImageView()
    {
        InitializeComponent();
        _PathList = new List<string>();
        _CheckPathList = new List<string>();
        _Index = 0;
    }
    private void ViewPageLoaded(Object sender, EventArgs e)
    {
        _FolderNames = new List<string> { Constants.AdsFolderFirst, 
                                          Constants.AdsFolderSecond };
        _Backend = new Backend();

        _MainWindow = (MainWindow)Window.GetWindow(this);

        _ImageTimer = new Timer(Constants.DefaultImageTimer);
        _ImageTimer.Elapsed += ChangeImageSource;
        _ImageTimer.Start();

    }

    private void ChangeImageSource(object sender, System.Timers.ElapsedEventArgs e)
    {
        Application.Current.Dispatcher.Invoke(
            DispatcherPriority.Normal, new Action(
                  delegate()
                  {
                      try
                      {
                          if (MainImage != null && MainImage.Source != null)
                          {
                              MainImage.Source = null;
                          }
                          if (VideoControl != null && VideoControl.Source != null)
                          {
                              VideoControl.Stop();
                              VideoControl.Source = null;
                          }
                          if (_Index >= _PathList.Count)
                          {
                              _Index = 0;
                          }
                          if (_PathList.ElementAt(_Index) != null)
                          {
                              Log.Info(String.Format("Start [ChangeImageSource]. Element: {0}, Index: {1}", _PathList.ElementAt(_Index), _Index));
                              try
                              {
                                  _ImageTimer.Stop();
                                  String[] checkExt = _PathList.ElementAt(_Index).Split('.');
                                  String ext = checkExt[checkExt.Length - 1];
                                  if (ext.Equals("jpg", StringComparison.CurrentCultureIgnoreCase) ||
                                      ext.Equals("jpeg", StringComparison.CurrentCultureIgnoreCase) ||
                                      ext.Equals("png", StringComparison.CurrentCultureIgnoreCase))
                                  {
                                      _ImageTimer.Interval = Constants.NormalImageTimer;
                                      ShowImage(_PathList.ElementAt(_Index));
                                  }
                                  else if (ext.Equals("mp4", StringComparison.CurrentCultureIgnoreCase) ||
                                           ext.Equals("3gp", StringComparison.CurrentCultureIgnoreCase))
                                  {
                                      _ImageTimer.Interval = Constants.VideoDefaultTimer;
                                      PlayQueue(_PathList.ElementAt(_Index));
                                  }
                                  _ImageTimer.Start();
                                  _Index++;
                              }
                              catch (Exception exception)
                              {
                                  Log.ErrorException(exception.Message, exception);
                              }
                          }
                      }
                      catch (Exception exception)
                      {
                          Log.ErrorException(exception.Message, exception);
                      }
                  }));
    }

    private void ShowImage(String fileName)
    {
        try
        {
            if (!String.IsNullOrEmpty(fileName))
            {
                _BitmapImage = LoadImage(fileName);
                MainImage.Source = _BitmapImage;
            }
        }
        catch (Exception e)
        {
            Log.ErrorException(e.Message, e);
        }
    }

    private void PlayQueue(String fileName)
    {
        try
        {
            if (!String.IsNullOrEmpty(fileName))
            {
                VideoControl.LoadedBehavior = MediaState.Play;
                VideoControl.Source = new Uri(fileName, UriKind.Absolute);
            }
        }
        catch (Exception e)
        {
            Log.ErrorException(e.Message, e);
        }
    }

    private void MediaEnded(object sender, EventArgs e)
    {
        try
        {
            if (MainImage != null && MainImage.Source != null)
            {
                MainImage.Source = null;
            }
            if (VideoControl != null && VideoControl.Source != null)
            {
                VideoControl.Stop();
                VideoControl.Source = null;
            }
            if (_Index >= _PathList.Count)
            {
                _Index = 0;
            }
            if (_PathList.ElementAt(_Index) != null)
            {
                Log.Info(String.Format("Start [MediaEnded oper]. Element: {0}, Index: {1}", _PathList.ElementAt(_Index), _Index));
                try
                {
                    _ImageTimer.Stop();
                    String[] checkExt = _PathList.ElementAt(_Index).Split('.');
                    String ext = checkExt[checkExt.Length - 1];
                    if (ext.Equals("jpg", StringComparison.CurrentCultureIgnoreCase) ||
                        ext.Equals("jpeg", StringComparison.CurrentCultureIgnoreCase) ||
                        ext.Equals("png", StringComparison.CurrentCultureIgnoreCase))
                    {
                        _ImageTimer.Interval = Constants.NormalImageTimer;
                        ShowImage(_PathList.ElementAt(_Index));
                    }
                    else if (ext.Equals("mp4", StringComparison.CurrentCultureIgnoreCase) ||
                             ext.Equals("3gp", StringComparison.CurrentCultureIgnoreCase))
                    {
                        _ImageTimer.Interval = Constants.VideoDefaultTimer;
                        PlayQueue(_PathList.ElementAt(_Index));
                    }
                    _ImageTimer.Start();
                    _Index++;
                }
                catch (Exception exception)
                {
                    Log.ErrorException(exception.Message, exception);
                }
            }
        }
        catch (Exception exception)
        {
            Log.ErrorException(exception.Message, exception);
        }
    }

    private void MediaOpened(object sender, EventArgs e)
    {
    }

    private BitmapImage LoadImage(string myImageFile)
    {
        BitmapImage myRetVal = null;
        if (!String.IsNullOrEmpty(myImageFile))
        {
            var image = new BitmapImage();
            try
            {
                using (FileStream stream = File.OpenRead(myImageFile))
                {
                    image.BeginInit();
                    image.CacheOption = BitmapCacheOption.OnLoad;
                    image.StreamSource = stream;
                    image.EndInit();
                }
            }
            catch (Exception exception)
            {
                Log.ErrorException(exception.Message, exception);
            }
            myRetVal = image;
        }
        return myRetVal;
    }

WPF MediaElement视频冻结

我在谷歌上搜索了一下,发现这是与软件渲染有关的WPF图形问题。通过将这段代码添加到ViewPageLoaded方法中,可以解决该问题。

        try
        {
            var hwndSource = PresentationSource.FromVisual(this) as HwndSource;
            var hwndTarget = hwndSource.CompositionTarget;
            hwndTarget.RenderMode = RenderMode.SoftwareOnly;
        }
        catch (Exception ex)
        {
            Log.ErrorException(ex.Message, ex);
        }

它帮助我解决了这个问题。希望它也能帮助你。

从这里得到了答案。感谢@detale的解决方案

这是一个复杂的问题。。我会尽力深入解释的。(是的,我有一个解决方案给你)

让我们从MediaElement应该能够做什么开始 不,真的

这是一个通配符,对吧?意味着你扔在上面的东西-需要播放:视频、图片、动画礼物、音乐。。好的…
现在每个类别都有多种格式(或标准)。。Gif,Png。。Wmv、Mp4…
因此,我们使用的每个文件都是由其他编辑器创建的
(有一个玩家可以在里面玩——这是肯定的。)

似乎大多数公司都在削减开支——他们并不总是(通常是…)全面实施标准。。因此,我们得到的结果文件并不总是与标准文件1:1。

因此,对于一个玩家来说,什么是完美的文件格式,对于另一个玩家而言,可能会被认为过于损坏

虽然商业/高级播放器被设计为能够容忍用某种标准编写的文件的损坏和"味道"——MediaElement——嗯。。更简单,也许与你可能扔给它玩的东西相比,它太简单了。

所以当它陷入这种类型的问题时——是的。。它可能会冻结,不会报告——这是我可以完全责怪微软的事情——为什么?因为冻结是可以接受的缺陷,但忽略它而不通知使用MediaElement的程序它冻结或遇到严重的演示错误是不可接受的(也是极不负责任的!)
但正如我所说,这是微软的问题,绝对不是你的错。

那么解决方案是什么呢?

你可能会试着对自己说:"好吧,我只需要另一个组件来播放视频或使用第三方插件。",但不,我的朋友,这样做并不能真正解决你的问题,因为你不知道你要替换的东西是否会遇到完全相同的问题。。

因此,你唯一的选择就是创建自己的"自定义"标准-放松,我的意思不是你需要开发一个新的标准-我只是说你需要创建一个标准战术,以确保你要在MediaElement上扔的东西不会被冻结。。

因此,如果你的应用程序将播放用作资源的视频-你可能想使用最新版本的AnyVideoConverter将所有视频转换为mp4。对我来说,它运行得相当好,在wmv中冻结的视频转换为mp4,现在MediaElement非常顺利地容忍了它。这不是MP4,而是转换本身-我相信ANV创建了一个"现代化"的视频文件,可以使用任何标准的文件。

然而,如果你的视频在运行时是动态的/上传到你的应用程序或类似的东西,你必须确保将你的应用即将运行的任何视频传递给你选择的"标准化器",然后你才能真正将它们扔到MediaElement。

顺便说一下,浏览器偶尔也会遇到同样的问题

我只是希望所有这些都能为其他遇到它的人解决问题。

您正在创建许多BitmapImage实例,BitmapImage类中存在内存泄漏,BitmapImage保留了对源流的引用(大概是为了让您可以随时读取StreamSource属性),因此它使MemoryStream对象保持活动状态。这导致内存不足异常。阅读这篇文章,他为流创建了一个很好的包装器,它对我有效。

他在wrapperclass中创建了一个流的实例,当你调用wrapper和BitmapImage的dispose方法时,这个实例就会被丢弃。Source只有一个空的wrapper类,它对原始流没有任何引用。

我建议注册MediaElement.MediaFailed事件。看看它是否会给你带来任何回报。

然而,正如其他人所提到的,这听起来像是一个记忆问题。您可以使用WPF性能套件,甚至只使用任务管理器来确认这一点。注意内存使用量的逐渐增加。

正如Shivam cv所提到的,这可能是BitmapImage的泄漏。试着把它从你的解决方案中注释出来,看看这是否解决了问题。