WPF MediaElement 视频冻结

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23075609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 11:32:30  来源:igfitidea点击:

WPF MediaElement Video freezes

c#wpfwindowsmediaelement

提问by 0bj3ct

I am using Image and MediaElement in wpf project, where I show Images and Videos from file system. I have few timers, which load files to Image/MediaElement controls. Everything works for 4-5 hours, but then MediaElement Video file freezes and MediaEnded event does not occur. I restart the application, it runs without any problem, but after some hours this problem occurs again.

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

My WPF XAML code:

我的 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# code:

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

采纳答案by 0bj3ct

I googled it and found that this was WPF graphic issue related to software rendering. The issue is solved by adding this piece of code into the ViewPageLoadedmethod.

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

It helped me to solve the problem. Hope it will help you too.

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

Got the answer from here. Thanks to @detale for the solution

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

回答by G.Y

This is a complicated problem.. I'll try to explain it in depth. (and yes I have a solution for you)

这是一个复杂的问题..我会尝试深入解释它。(是的,我为您提供了解决方案)

lets start with What MediaElement should be capable of doing?no.. really!

Its a wildcard right? meaning that what ever you throw on it - needs to be played: Videos, Pictures, Animated Gifs, Music.. Ok..
Now.. Each of those categories has multiple Formats (or standards).. Gif,Png.. Wmv,Mp4...
And so, each of those files we use was created by some other editor
(which has a player that can play it implemented inside - that's for sure..)

It seems that most of the companies cut expenses - they don't always (usually that is..) implement a standard in full.. so what we get as result files are not always 1:1 to the standard.

让我们从MediaElement 应该能够做什么开始?不完全是!

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

似乎大多数公司都削减了开支——他们没有” t 总是(通常是..)完全实现一个标准..所以我们得到的结果文件并不总是与标准1:1。

So what is perfect file format for one player can be considered as too corrupted for another player.

那么对于一个播放器来说什么是完美的文件格式对于另一个播放器来说可以被认为是太损坏了。

And while the commercial/advanced players are designed to be tolerant to corruptions and "flavors" of a file written in some standard - MediaElement - well.. is more simplistic and perhaps it is too simplistic compared to what you may throw at it to play.

虽然商业/高级播放器被设计为可以容忍以某种标准编写的文件的损坏和“风味” - MediaElement - 好吧.. 更简单,也许与您可能投入的内容相比,它过于简单了.

So when it stump into that type of problem - yes.. it may freeze and will not report - and that is something that I can blame Microsoft in full - and why? because it is an acceptable defect to freeze but it is not acceptable (and extremely irresponsible!) to ignore it and not notify the program which using the MediaElement that it frozen or encountered a serious presentation error..
But as I said, this is Microsoft problem and definitely not your fault.

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

So what are the solutions?

那么有哪些解决方法呢?

You may try to say to yourself "Fine - I'll just get another component to play videos or use a 3rd party plug-in..", But no my friend, Doing that will not really solve your problem as you do not know if what you about to put in replace would not suffer from the exact same problem..

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

So the only option you are left with is to create your own "custom" standard - relax, I do not mean you need to develop a new standard - I just mean you need to create a standard tactic to make sure that what you going to throw on the MediaElement will be played with no freezes..

所以你剩下的唯一选择就是创建你自己的“自定义”标准——放松,我不是说你需要开发一个新标准——我只是说你需要创建一个标准策略来确保你要做什么投掷 MediaElement 将在不冻结的情况下播放..

So, If your app going to play videos which are being used as resources - You may want to use in example the latest version of AnyVideoConverter to convert all your videos to mp4. For me it worked rather well, videos which were freezing in wmv converted to mp4 and are now tolerated by the MediaElement very smoothly. it is not the MP4 that did the trick but the conversion itself - I believe that ANV creates a "modernized" video file of any of the standards you may use for your files.

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

However, If your videos are dynamic/uploaded to your app during runtime or something like that - you will have to make sure to pass any video your app about to run through what you choose as "standardizer" before you can actually throw them at the MediaElement.

但是,如果您的视频在运行时或类似情况下是动态的/上传到您的应用程序 - 您必须确保通过您的应用程序将运行的任何视频通过您选择的“标准化器”,然后才能真正将它们扔到媒体元素。

By the way, Browsers suffer from the same problem occasionally.

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

I just hope all this may sort the problem for anyone else who encountered it.

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

回答by Shivam cv

You are creating lots of BitmapImage instances, there is memory leak in BitmapImage class, The BitmapImage keeps a reference to the source stream (presumably so that you can read the StreamSource property at any time), so it keeps the MemoryStream object alive. This is causing Memory out exception. Read this, he has created a nice wrapper for stream, It worked for me.

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

He created an Instance of stream in wrapperclass which get disposed when you call dispose method of wrapper and BitmapImage.Source only has empty wrapper class which doesn't have any reference to original stream.

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

回答by Muhammad Rehan Saeed

I would suggest registering for the MediaElement.MediaFailedevent. See if it returns anything to you.

我建议注册MediaElement.MediaFailed事件。看看它是否会向您返回任何东西。

However, as others have mentioned this sounds like a memory issue. You can use the WPF Performance Suiteor even just the task manager to confirm this. Watch for a very gradual increase in memory usage.

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

As Shivam cv has mentioned, it could be the BitmapImage is the leak. Try commenting it out of your solution and see if that fixes the issue.

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