在 Windows IoT 上播放视频时出现内存泄漏 | UWP
Memory Leak playing videos on Windows IoT | UWP
我构建了一个应用程序,可以从 USB 驱动器读取视频文件并使用物理按钮在它们之间切换。该应用程序在一段时间内运行良好,但一段时间后设备(DragonBoard 410c,最新 Windows Insider Preview Build 15051)由于应用程序消耗了所有内存而崩溃。
查看设备门户中的进程,我可以看到每次切换视频文件时 "Working Set" 内存跳跃,而 "Private Working Set" 大致保持不变(大约 30MB)。
这是我加载视频文件的方式:
C#
private IReadOnlyList<StorageFile> _videofiles
// list all available video files
public void Init(){
var queryOptions = new QueryOptions();
queryOptions.FolderDepth = depth;
foreach (var fileType in fileTypes)
{
queryOptions.FileTypeFilter.Add(fileType);
}
var query = KnownFolders.RemovableDevices.CreateFileQueryWithOptions(queryOptions);
_videofiles = await query.GetFilesAsync();
}
private async void SelectVideo(int videoId)
{
StorageFile videofile = _videofiles.Where(x => x.DisplayName == videoId.ToString()).FirstOrDefault();
if (videofile != null)
{
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
var stream = await videofile.OpenAsync(FileAccessMode.Read);
VideoPlayer.SetSource(stream, videofile.FileType);
}
}
// since the button interrupt is not on the UI thread, SelectVideo() is called like this
private async void SelectVideoMarshalled(int videoId)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
SelectVideo(videoId);
});
}
XAML
<ContentControl x:Name="VideoPlayer" Content="{x:Bind ViewModel.VideoPlayer, Mode=OneWay}"/>
我已经在几个地方手动尝试了 运行 GC.Collect(),但还没有成功。有什么想法吗?
因为你有一个 StorageFile
对象,我建议使用 Source
属性 和文件的 Path
而不是 SetSource
并打开 Stream
手动。
此外,您应该始终在使用完 MediaElement 后将其清空(最好在 OnNavigatingFrom 中完成)。
这是您的代码,经过简化:
private void SelectVideo(string videoId)
{
var videofile = _videofiles.FirstOrDefault(x => x.DisplayName == videoId.ToString());
if (videofile == null) return;
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
VideoPlayer.Source = new Uri(videofile.Path);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
VideoPlayer.Stop();
VideoPlayer.Source = null;
base.OnNavigatedFrom(e);
}
我还有一个旁注,您可以 x:Bind
ViewModel 的事件处理程序。
例如,如果您的视频文件列表是 ListView
字符串:
public void VideosListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e?.AddedItems?.Count > 0)
{
var fileDisplayName = e.AddedItems.FirstOrDefault() as string;
if (!string.IsNullOrEmpty(fileDisplayName))
SelectVideo(fileDisplayName);
}
}
注意我只需要将方法签名更改为 public
然后在 XAML 中你可以这样做:
<ListView ItemsSource="{x:Bind ViewModel.VideoFiles, Mode=OneTime}"
SelectionChanged="{x:Bind ViewModel.VideosListView_OnSelectionChanged}"/>
无需编组回 UI 线程 :)
最后,您可以查看 the demo here on GitHub 我在其中实现了类似的东西。
事实证明我的代码毕竟没问题。我有几次 Windows 更新卡住/失败,但我没有注意到。
当更新最终成功完成时,内存泄漏消失了。
我构建了一个应用程序,可以从 USB 驱动器读取视频文件并使用物理按钮在它们之间切换。该应用程序在一段时间内运行良好,但一段时间后设备(DragonBoard 410c,最新 Windows Insider Preview Build 15051)由于应用程序消耗了所有内存而崩溃。
查看设备门户中的进程,我可以看到每次切换视频文件时 "Working Set" 内存跳跃,而 "Private Working Set" 大致保持不变(大约 30MB)。
这是我加载视频文件的方式:
C#
private IReadOnlyList<StorageFile> _videofiles
// list all available video files
public void Init(){
var queryOptions = new QueryOptions();
queryOptions.FolderDepth = depth;
foreach (var fileType in fileTypes)
{
queryOptions.FileTypeFilter.Add(fileType);
}
var query = KnownFolders.RemovableDevices.CreateFileQueryWithOptions(queryOptions);
_videofiles = await query.GetFilesAsync();
}
private async void SelectVideo(int videoId)
{
StorageFile videofile = _videofiles.Where(x => x.DisplayName == videoId.ToString()).FirstOrDefault();
if (videofile != null)
{
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
var stream = await videofile.OpenAsync(FileAccessMode.Read);
VideoPlayer.SetSource(stream, videofile.FileType);
}
}
// since the button interrupt is not on the UI thread, SelectVideo() is called like this
private async void SelectVideoMarshalled(int videoId)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
SelectVideo(videoId);
});
}
XAML
<ContentControl x:Name="VideoPlayer" Content="{x:Bind ViewModel.VideoPlayer, Mode=OneWay}"/>
我已经在几个地方手动尝试了 运行 GC.Collect(),但还没有成功。有什么想法吗?
因为你有一个 StorageFile
对象,我建议使用 Source
属性 和文件的 Path
而不是 SetSource
并打开 Stream
手动。
此外,您应该始终在使用完 MediaElement 后将其清空(最好在 OnNavigatingFrom 中完成)。
这是您的代码,经过简化:
private void SelectVideo(string videoId)
{
var videofile = _videofiles.FirstOrDefault(x => x.DisplayName == videoId.ToString());
if (videofile == null) return;
Debug.WriteLine($"Video {videofile.DisplayName} was selected");
VideoPlayer.Source = new Uri(videofile.Path);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
VideoPlayer.Stop();
VideoPlayer.Source = null;
base.OnNavigatedFrom(e);
}
我还有一个旁注,您可以 x:Bind
ViewModel 的事件处理程序。
例如,如果您的视频文件列表是 ListView
字符串:
public void VideosListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e?.AddedItems?.Count > 0)
{
var fileDisplayName = e.AddedItems.FirstOrDefault() as string;
if (!string.IsNullOrEmpty(fileDisplayName))
SelectVideo(fileDisplayName);
}
}
注意我只需要将方法签名更改为 public
然后在 XAML 中你可以这样做:
<ListView ItemsSource="{x:Bind ViewModel.VideoFiles, Mode=OneTime}"
SelectionChanged="{x:Bind ViewModel.VideosListView_OnSelectionChanged}"/>
无需编组回 UI 线程 :)
最后,您可以查看 the demo here on GitHub 我在其中实现了类似的东西。
事实证明我的代码毕竟没问题。我有几次 Windows 更新卡住/失败,但我没有注意到。 当更新最终成功完成时,内存泄漏消失了。