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图形问题。通过将这段代码添加到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的泄漏。试着把它从你的解决方案中注释出来,看看这是否解决了问题。