WPF MediaElement Video freezes
Asked Answered
N

4

6

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.

My WPF XAML code:

<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:

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;
    }
Naos answered 15/4, 2014 at 5:52 Comment(5)
I suspect some memory issue. But it is difficult to answer just by analyzing the code in question. Could you send us the sample video(s) you are trying to play? appreciated if you could send a whole sample package which can reproduce the issue?Gaivn
@Gaivn you can replicate by simply calling mediaElement.Play() and when you receive the MediaEnded event notification load another video using the same mediaElement and call Play(). Set up a loop with 2 video's and when one ends then start the next one, just let it run continuously until one of the video's freezes and MediaEnded never gets called. It also fails if you just loop on on video. Usually takes 6 to 12 hours before it freezes and app shows no additional memory usage over when it is started.Inappetence
I did exactly same. I set up 2 videos in the path list and since my last message they are being played for 10 sec in a loop. lets see if the issue is reproducible. BTW I would like to mention that the code is bit old as compared to today's practice. You may perhaps leverage DispatcherTimer and Tasks for some simplification.Gaivn
I did run it for a quite long time (over 24+ hours), but I am not able to reproduce the same. is there something to do with the video coded on your machine or the video itself? I did the test with two .avi files. try re-installing video codec or changing the encoding of the videos and see if the problem still remains. if you still face the issue, then perhaps you may send a sample copy of your code with the videos which you think can reproduce the issue.Gaivn
A dirty solution is to provide a scheduled restart. You can also try to poll with short blank movie in between of normal ones, as soon as you get no MediaEnded - perform restart.Eduard
N
6

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 ViewPageLoaded method.

        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

Naos answered 30/7, 2014 at 5:32 Comment(4)
That is not solving the problem in my case.. and no wonder too as the referenced answer looks like Try 1,2,3.. and does not get to the root of the problem nor explain why the problem occur in the first place.Hebetic
Note that RenderMode.SoftwareOnly bypasses the computer GPU and graphics card hardware. All video processing is done by the CPU. Expect a noticable increase in CPU usage from this solution. I have also noticed video frames being dropped when using this approach producing a jittery video at times.Antibiosis
it looks like my problem is solved... Hallelujah. I was rendering MP4 files located on a file share. Some freezed, Sometimes only sound, gary images, ... sometime the video was good.???Archducal
For me, this solution started to work when I've integrated the above code, not only in the Window_Loaded method, but also in the button click event handler, where I was calling Media.Play() method.Boudreau
H
5

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.

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.

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.

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..

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..

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.

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.

Hebetic answered 7/10, 2014 at 17:26 Comment(0)
R
2

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.

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.

Rosado answered 17/7, 2014 at 11:57 Comment(6)
Not sure if this is the issue given the video freezes and never returns the MediaEnded event. How does this relate to the BitmapImage ?Inappetence
I have not compiled your code, but as you said problem arises after 4-5 hrs, so must be memory piling up. And only source of new instances is LoadImage method of yoursRosado
I have the same problem but this is not my code... I just have videoInappetence
Without looking at code nobody can suggest any solution, so please post your code. MediaElement has no such know issue.Rosado
If I get time I will create a sample app and test this and post the code. Was hoping there was a quick fix for this. My app has lots of background threads doing all kinds of stuff including pausing the video and making the media element hidden/visible so I guess it could be a combination of things. Having said that it only freezes when the app has been doing nothing other than cycling the videos for hours - so I think its something deep in the MediaElement code.Inappetence
There is no memory leak - I tried it with video size of 325kb and it froze after 32 seconds. (just after the 3rd loop) something deeper is going on there.Hebetic
G
1

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

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

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.

Gethsemane answered 18/7, 2014 at 15:47 Comment(2)
Duncan already pointed out that there is no increase in memory usage. This is something else, like maybe not releasing file handles.Acarology
I have registered for the MediaFailed event but that does not get called. In my code I don't do any bitmap stuff at all, just cycling through a directory of video files. Everything else works in the app and if I reload a media file it works too - the problem is my code relies on the MediaEnded event to know when to start the next one and this never gets called. I will create a sample app and post that as another question - just don't have time right now so was hoping someone had a quick fix...Inappetence

© 2022 - 2024 — McMap. All rights reserved.