【问题标题】:WPF MediaElement Video freezesWPF MediaElement 视频冻结
【发布时间】:2014-05-29 07:53:34
【问题描述】:

我在 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;
    }

【问题讨论】:

  • 我怀疑存在内存问题。但是仅通过分析有问题的代码很难回答。您能否将您尝试播放的示例视频发送给我们?如果您能发送一个可以重现该问题的完整示例包,将不胜感激?
  • @pushpraj 您可以通过简单地调用 mediaElement.Play() 进行复制,当您收到 MediaEnded 事件通知时,使用相同的 mediaElement 加载另一个视频并调用 Play()。设置一个包含 2 个视频的循环,当一个结束然后开始下一个,让它连续运行,直到其中一个视频冻结并且 MediaEnded 永远不会被调用。如果您只是循环播放视频,它也会失败。通常需要 6 到 12 小时才能冻结,并且应用程序在启动时没有显示额外的内存使用情况。
  • 我做的完全一样。我在路径列表中设置了 2 个视频,自从我上一条消息以来,它们循环播放了 10 秒。让我们看看问题是否可重现。顺便说一句,我想提一下,与今天的实践相比,代码有点旧。您也许可以利用DispatcherTimerTasks 进行一些简化。
  • 我确实运行了很长时间(超过 24 小时以上),但我无法重现相同的内容。与您机器上编码的视频或视频本身有关吗?我用两个 .avi 文件进行了测试。尝试重新安装视频编解码器或更改视频的编码,看看问题是否仍然存在。如果您仍然遇到问题,那么也许您可以发送代码的示例副本以及您认为可以重现问题的视频。
  • 一个肮脏的解决方案是提供预定的重启。您也可以尝试poll,在正常电影之间使用短片空白电影,只要您没有得到MediaEnded - 执行重新启动。

标签: c# wpf windows mediaelement


【解决方案1】:

我搜索了一下,发现这是与软件渲染相关的 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);
        }

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

here 得到答案。感谢@detale 的解决方案

【讨论】:

  • 这并不能解决我的问题.. 也难怪引用的答案看起来像 Try 1,2,3.. 并没有找到问题的根源,也没有解释原因问题首先出现。
  • 请注意,RenderMode.SoftwareOnly 会绕过计算机 GPU 和显卡硬件。所有视频处理均由 CPU 完成。预计此解决方案会显着增加 CPU 使用率。我还注意到,使用这种方法有时会产生抖动的视频时会丢失视频帧。
  • 看来我的问题已经解决了……哈利路亚。我正在渲染位于文件共享上的 MP4 文件。有些冻结,有时只有声音,加里图像,...有时视频很好。???
  • 对我来说,当我集成上述代码时,这个解决方案开始起作用,不仅在 Window_Loaded 方法中,而且在我调用 Media.Play() 的按钮单击事件处理程序中方法。
【解决方案2】:

这是一个复杂的问题。我将尝试深入解释它。 (是的,我有一个解决方案)

让我们从 MediaElement 应该能够做什么? 不.. 真的!

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

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

因此,对于一个播放器来说,完美的文件格式对于另一个播放器来说可能被认为太损坏了。

虽然商业/高级播放器被设计为容忍以某些标准编写的文件的损坏和“风味” - MediaElement - 嗯..更简单,与您可能抛出的相比可能过于简单它可以播放。

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

那么有什么解决办法呢?

您可能会尝试对自己说“好吧 - 我将获得另一个组件来播放视频或使用 3rd 方插件..”,但不是我的朋友,这样做并不能真正解决您的问题不知道您要替换的内容是否会遇到完全相同的问题..

因此,您剩下的唯一选择就是创建自己的“自定义”标准 - 放松,我并不是说您需要制定新标准 - 我只是说您需要创建一个标准策略来确保你要扔在 MediaElement 上的播放将不会冻结..

因此,如果您的应用要播放被用作资源的视频 - 您可能希望使用最新版本的 AnyVideoConverter 将所有视频转换为 mp4。对我来说,它工作得相当好,在 wmv 中冻结的视频转换为 mp4,现在 MediaElement 可以非常顺利地容忍。发挥作用的不是 MP4,而是转换本身——我相信 ANV 会创建一个“现代化”的视频文件,符合您可能用于文件的任何标准。

但是,如果您的视频在运行时或类似情况下是动态的/上传到您的应用程序 - 您必须确保通过您选择的“标准化程序”将您的应用程序运行的任何视频传递给您,然后才能实际抛出他们在 MediaElement。

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

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

【讨论】:

    【解决方案3】:

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

    他在 wrapperclass 中创建了一个流实例,当您调用 wrapper 的 dispose 方法时,它会被释放,而 BitmapImage.Source 只有空的包装器类,它没有对原始流的任何引用。

    【讨论】:

    • 不确定这是否是问题,因为视频冻结并且永远不会返回 MediaEnded 事件。这与 BitmapImage 有什么关系?
    • 我还没有编译你的代码,但正如你所说的4-5小时后出现问题,所以一定是内存堆积。新实例的唯一来源是您的 LoadImage 方法
    • 我有同样的问题,但这不是我的代码...我只有视频
    • 不看代码没有人可以提出任何解决方案,所以请发布您的代码。 MediaElement 没有这样的已知问题。
    • 如果有时间,我将创建一个示例应用程序并对其进行测试并发布代码。希望有一个快速解决这个问题。我的应用程序有很多后台线程做各种各样的事情,包括暂停视频和使媒体元素隐藏/可见,所以我猜它可能是一些事情的组合。话虽如此,它只会在应用程序除了循环播放视频数小时之外什么都不做时冻结 - 所以我认为它在 MediaElement 代码中很深。
    【解决方案4】:

    我建议注册MediaElement.MediaFailed 活动。看看它是否会返回任何东西给你。

    但是,正如其他人所提到的,这听起来像是内存问题。您可以使用WPF Performance Suite 甚至只是任务管理器来确认这一点。注意内存使用量的逐渐增加。

    正如 Shivam cv 所提到的,它可能是 BitmapImage 是泄漏。尝试将其从您的解决方案中注释掉,看看是否能解决问题。

    【讨论】:

    • Duncan 已经指出内存使用量并没有增加。这是另外一回事,比如可能不释放文件句柄。
    • 我已经注册了 MediaFailed 事件,但没有被调用。在我的代码中,我根本不做任何位图的事情,只是循环浏览一个视频文件目录。其他一切都在应用程序中工作,如果我重新加载媒体文件,它也可以工作 - 问题是我的代码依赖于 MediaEnded 事件来知道何时开始下一个,而这永远不会被调用。我将创建一个示例应用程序并将其作为另一个问题发布 - 只是现在没有时间,所以希望有人能快速修复......
    猜你喜欢
    • 2013-04-11
    • 2015-08-13
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多