【问题标题】:Memory Leak playing videos on Windows IoT | UWP内存泄漏在 Windows IoT 上播放视频 | UWP
【发布时间】:2017-04-26 08:28:54
【问题描述】:

我已经构建了一个可以从 USB 驱动器读取视频文件并使用物理按钮在它们之间切换的应用程序。该应用程序在一段时间内运行良好,但在一段时间后,设备(DragonBoard 410c,最新的 Windows Insider Preview Build 15051)由于应用程序消耗了所有内存而崩溃。

查看设备门户中的进程,我可以看到每次切换视频文件时“工作集”内存都会跳跃,而“私有工作集”大致保持不变(大约 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(),但还没有成功。有什么想法吗?

【问题讨论】:

  • 只是一个快速的想法,但每个流都是一次性的,因此您应该在将之前设置为VideoPlayer 源的流设置为SelectVideo 中的新流实例后将其处理掉方法,
  • 调用stream.Dispose();在 VideoPlayer.SetSource() 之后(这是发生内存峰值的地方)没有帮助。
  • 即使导航到不同的页面也无助于释放内存
  • 是的,它没有帮助,因为您正在处理流的当前实例 - 您希望在播放器中的那个实例。您应该处置前一个实例 - 当您在其上调用 SetSource 方法时,该实例将从 VideoPlayer 中删除。

标签: c# memory-leaks uwp windowsiot windows-iot-core-10


【解决方案1】:

由于您有一个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 更新,但很高兴看到您应该如何做,我仍然会尝试在我的代码中包含这些最佳实践。
【解决方案2】:

结果证明我的代码还是没问题的。我有几次我没有注意到的 Windows 更新卡住/失败。 当更新最终成功完成时,内存泄漏就消失了。

【讨论】:

    猜你喜欢
    • 2011-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多