Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix various memory leaks #41

Merged
merged 2 commits into from
Dec 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion OneDrive-Cloud-Player/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
Expand All @@ -22,7 +24,7 @@ sealed partial class App : Application
{
//Other classes can now call this class with the use of 'App.Current'.
public static new App Current => (App)Application.Current;

public CoreDispatcher UIDispatcher { get; private set; }
public IPublicClientApplication PublicClientApplication { get; private set; }
public string[] Scopes { get; private set; }
public CacheHelper CacheHelper { get; private set; }
Expand Down Expand Up @@ -111,6 +113,8 @@ protected override async void OnLaunched(LaunchActivatedEventArgs e)
// Ensure the current window is active
Window.Current.Activate();
}

UIDispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
}

/// <summary>
Expand Down
144 changes: 79 additions & 65 deletions OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ public class VideoPlayerPageViewModel : ViewModelBase, INotifyPropertyChanged, I
private readonly ApplicationDataContainer localMediaVolumeLevelSetting;
private readonly INavigationService _navigationService;
private readonly GraphHelper graphHelper = GraphHelper.Instance();
private readonly Timer fileNameOverlayTimer = new Timer();
/// <summary>
/// Fires every two minutes to indicate the OneDrive download URL has expired.
/// </summary>
private readonly Timer reloadIntervalTimer = new Timer(120000);
/// <summary>
/// Single-shot timer to hide the filename shortly after playing a video.
/// </summary>
private readonly Timer fileNameOverlayTimer = new Timer(5000);
private MediaWrapper MediaWrapper = null;
private bool InvalidOneDriveSession = false;
private Timer reloadIntervalTimer;
private MediaPlayer mediaPlayer;
private int MediaListIndex;

Expand Down Expand Up @@ -227,70 +233,76 @@ private async void InitializeLibVLC(InitializedEventArgs eventArgs)
VideoLength = 0;
PlayPauseButtonFontIcon = "\xE768";

CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;

// Create LibVLC instance and subscribe to events.
LibVLC = new LibVLC(eventArgs.SwapChainOptions);
MediaPlayer = new MediaPlayer(LibVLC);

// Initialize timers.
// Create a timer that fires the elapsed event when its time to retrieve
// and play the media from a new OneDrive download URL (2 minutes).
reloadIntervalTimer = new Timer(120000);
// Hook up the Elapsed event for the timer.
reloadIntervalTimer.Elapsed += (sender, e) =>
{
InvalidOneDriveSession = true;
};
reloadIntervalTimer.Enabled = true;

/*
* Subscribing to LibVLC events.
*/
//Subscribes to the Playing event.
MediaPlayer.Playing += async (sender, args) =>
{
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Media is playing");
await dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
MediaVolumeLevel = (int)this.localMediaVolumeLevelSetting.Values["MediaVolume"];
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Set volume in container: " + this.localMediaVolumeLevelSetting.Values["MediaVolume"]);
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Set volume in our property: " + MediaVolumeLevel);
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Actual volume: " + MediaPlayer.Volume);
//Sets the max value of the seekbar.
VideoLength = MediaPlayer.Length;
MediaPlayer.Playing += MediaPlayer_Playing;
MediaPlayer.Paused += MediaPlayer_Paused;
MediaPlayer.TimeChanged += MediaPlayer_TimeChanged;

PlayPauseButtonFontIcon = "\xE769";
});
};
// Subscribe to the timer events and start the reloadInterval timer.
fileNameOverlayTimer.Elapsed += FileNameOverlayTimer_Elapsed;
reloadIntervalTimer.Elapsed += ReloadIntervalTimer_Elapsed;
reloadIntervalTimer.Start();

//Subscribes to the Paused event.
MediaPlayer.Paused += async (sender, args) =>
// Finally, play the media.
await PlayMedia();
}

private async void MediaPlayer_Playing(object sender, EventArgs e)
{
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Media is playing");
await App.Current.UIDispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
await dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
PlayPauseButtonFontIcon = "\xE768";
});
};
MediaVolumeLevel = (int)this.localMediaVolumeLevelSetting.Values["MediaVolume"];
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Set volume in container: " + this.localMediaVolumeLevelSetting.Values["MediaVolume"]);
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Set volume in our property: " + MediaVolumeLevel);
Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + ": Actual volume: " + MediaPlayer.Volume);
//Sets the max value of the seekbar.
VideoLength = MediaPlayer.Length;

PlayPauseButtonFontIcon = "\xE769";
});
}

MediaPlayer.TimeChanged += async (sender, args) =>
private async void MediaPlayer_Paused(object sender, EventArgs e)
{
await App.Current.UIDispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
await dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
PlayPauseButtonFontIcon = "\xE768";
});
}

private async void MediaPlayer_TimeChanged(object sender, EventArgs e)
{
await App.Current.UIDispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
// Updates the value of the seekbar on TimeChanged event
// when the user is not seeking.
if (!IsSeeking)
{
// Updates the value of the seekbar on TimeChanged event
// when the user is not seeking.
if (!IsSeeking)
// Sometimes the MediaPlayer is already null upon
// navigating away and this still gets called.
if (MediaPlayer != null)
{
// Sometimes the MediaPlayer is already null upon
// navigating away and this still gets called.
if (MediaPlayer != null)
{
TimeLineValue = MediaPlayer.Time;
}
TimeLineValue = MediaPlayer.Time;
}
});
};
}
});
}

await PlayMedia();
private void ReloadIntervalTimer_Elapsed(object sender, ElapsedEventArgs e)
{
InvalidOneDriveSession = true;
}

private async void FileNameOverlayTimer_Elapsed(object sender, ElapsedEventArgs e)
{
await App.Current.UIDispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
FileNameOverlayVisiblity = Visibility.Collapsed;
});
}

/// <summary>
Expand Down Expand Up @@ -318,23 +330,17 @@ private async Task PlayMedia(long startTime = 0)

FileName = MediaWrapper.CachedDriveItem.Name;

CoreDispatcher dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;

FileNameOverlayVisiblity = Visibility.Visible;

fileNameOverlayTimer.Interval = 5000;
fileNameOverlayTimer.AutoReset = false;
fileNameOverlayTimer.Start();
fileNameOverlayTimer.Elapsed += async (sender, e) =>
{
await dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
{
FileNameOverlayVisiblity = Visibility.Collapsed;
});
};

string mediaDownloadURL = await RetrieveDownloadURLMedia(MediaWrapper);
// Play the OneDrive file.
MediaPlayer.Play(new Media(LibVLC, new Uri(mediaDownloadURL)));
using (Media media = new Media(LibVLC, new Uri(mediaDownloadURL)))
{
MediaPlayer.Play(media);
}

if (MediaPlayer is null)
{
Expand Down Expand Up @@ -602,6 +608,14 @@ public void Deactivate(object parameter)
/// </summary>
public void Dispose()
{
// Unsubscribe from event handlers.
MediaPlayer.Playing -= MediaPlayer_Playing;
MediaPlayer.Paused -= MediaPlayer_Paused;
MediaPlayer.TimeChanged -= MediaPlayer_TimeChanged;
reloadIntervalTimer.Elapsed -= ReloadIntervalTimer_Elapsed;
fileNameOverlayTimer.Elapsed -= FileNameOverlayTimer_Elapsed;

// Dispose of the LibVLC instance and the mediaplayer.
var mediaPlayer = MediaPlayer;
MediaPlayer = null;
mediaPlayer?.Dispose();
Expand Down
17 changes: 11 additions & 6 deletions OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using OneDrive_Cloud_Player.ViewModels;
using System;
using System.Diagnostics;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
Expand Down Expand Up @@ -211,12 +210,15 @@ private void ExitFullscreenMode()
{
view.ExitFullScreenMode();
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto;
// The SizeChanged event will be raised when the exit from full-screen mode is complete.
// The SizeChanged event will be raised when the exitfrom full-screen
// mode is complete.
}
}

/// <summary>
/// Gets called when a user presses down a key on the videoplayer page. When the key is not listed in the switch case, it calls the view model to handle the key event.
/// Gets called when a user presses down a key on the videoplayer page.
/// When the key is not listed in the switch case, it calls the viewmodel
/// to handle the key event.
/// </summary>
/// <param name="sender"></param>
/// <param name="keyEvent"></param>
Expand All @@ -243,7 +245,8 @@ void VideoPlayerPage_KeyDown(CoreWindow sender, KeyEventArgs keyEvent)
}

/// <summary>
/// Executes after the user navigates to this page. This is used to add an event handler for the keydown event on this page.
/// Executes after the user navigates to this page. This is used to add an
/// event handler for the keydown event on this page.
/// </summary>
/// <param name="e"></param>
protected override void OnNavigatedTo(NavigationEventArgs e)
Expand All @@ -254,14 +257,16 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
}

/// <summary>
/// Executes before the user navigates away from this page. This is used to remove an event handler for the keydown event on this page,
/// and exit out of fullscreen if the app is still in fullscreen mode.
/// Executes before the user navigates away from this page. This is used to
/// remove an event handler for the keydown event on this page, and exit out
/// of fullscreen if the app is still in fullscreen mode.
/// </summary>
/// <param name="e"></param>
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
ExitFullscreenMode();

pointerMovementDispatcherTimer.Tick -= PointerMovementDispatcherTimer_Tick;
Window.Current.CoreWindow.KeyDown -= VideoPlayerPage_KeyDown;

base.OnNavigatingFrom(e);
Expand Down