Created
August 13, 2021 00:07
-
-
Save pictos/49e11ea4546d3bb351b50f9b182be23d to your computer and use it in GitHub Desktop.
VLCService
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using LibVLCSharp.Shared; | |
using static MediaPlayerX.Helpers.GlobalResources; | |
using MediaPlayerX.Models; | |
using System; | |
using System.Threading.Tasks; | |
using Xamarin.Forms; | |
using MediaPlayerX.Helpers; | |
using System.Collections.ObjectModel; | |
#nullable enable | |
namespace MediaPlayerX.Services | |
{ | |
public sealed class MediaPlayerService | |
{ | |
static readonly Lazy<MediaPlayerService> mediaPlayerService = | |
new Lazy<MediaPlayerService>(() => new MediaPlayerService()); | |
public static MediaPlayerService Current => mediaPlayerService.Value; | |
readonly LibVLC libVLC; | |
readonly MediaPlayer mp; | |
public MediaFile CurrentMedia { get; private set; } | |
public ObservableRangeCollection<MediaFile> MediaFiles { get; } = new ObservableRangeCollection<MediaFile>(); | |
public bool IsPlaying => mp.IsPlaying; | |
MediaPlayerService() | |
{ | |
Core.Initialize(); | |
libVLC = new LibVLC(); | |
random = new Random(); | |
mp = new MediaPlayer(libVLC); | |
} | |
public Action<TimeSpan>? OnTimeChanged { get; set; } | |
public Action? OnMediaChanged { get; set; } | |
public Action<float>? OnPositionChanged { get; set; } | |
public Action? OnFinished { get; set; } | |
public Action<TimeSpan>? OnMediaInfo { get; set; } | |
public Action<bool>? OnIsPlayingChanged { get; set; } | |
//TODO improve this | |
public string TotalDuration => TimeSpan.FromMilliseconds(mp.Media?.Duration ?? 0).ToStringTime(); | |
public bool IsRandomMode { get; set; } | |
public bool IsRepeateMode { get; set; } | |
bool isInit; | |
readonly Random random; | |
void Init() | |
{ | |
// Utils.WhatThreadAmI(); | |
if (!isInit) | |
{ | |
isInit = true; | |
mp.TimeChanged += TimeChanged; | |
mp.PositionChanged += PositionChanged; | |
mp.LengthChanged += LengthChanged; | |
mp.EndReached += EndReached; | |
mp.Playing += Playing; | |
mp.Paused += Paused; | |
mp.MediaChanged += OnMpMediaChanged; | |
} | |
} | |
public void SetSong(ref MediaFile media) | |
{ | |
// Utils.WhatThreadAmI(); | |
if (!(mp.Media is null)) | |
Stop(); | |
mp.Media = new Media(libVLC, media.Path, GetType(media.IsLocal)); | |
mp.Media.AddOption(":no-video"); | |
mp.Play(); | |
CurrentMedia = media; | |
if (media.IsLocal && MediaFiles.Count == 0) | |
MediaFiles.AddRange(LocalMedias); | |
else if (!media.IsLocal && MediaFiles.Count == 0) | |
MediaFiles.Add(CurrentMedia); | |
Init(); | |
DependencyService.Get<INotification>().ShowNotification(); | |
static FromType GetType(bool isLocal) | |
{ | |
if (isLocal) | |
return FromType.FromPath; | |
else | |
return FromType.FromLocation; | |
} | |
} | |
public void Play() | |
{ | |
if (!IsPlaying) | |
mp.Play(); | |
else | |
mp.Pause(); | |
} | |
public void Forward(long offSet) => | |
mp.Time += offSet; | |
public void Rewind(long offSet) => | |
mp.Time -= offSet; | |
public void SetMediaTime(long time) => | |
mp.Time = time; | |
void Paused(object sender, EventArgs e) => | |
Task.Run(() => OnIsPlayingChanged?.Invoke(false)); | |
void Playing(object sender, EventArgs e) => | |
Task.Run(() => OnIsPlayingChanged?.Invoke(true)); | |
void EndReached(object sender, EventArgs e) => | |
Task.Run(() => | |
{ | |
OnFinished?.Invoke(); | |
NextMusic(); | |
}); | |
void LengthChanged(object sender, MediaPlayerLengthChangedEventArgs e) => | |
Task.Run(() => | |
{ | |
if (e.Length > 0) | |
{ | |
var duration = TimeSpan.FromMilliseconds(e.Length); | |
OnMediaInfo?.Invoke(duration); | |
mp.LengthChanged -= LengthChanged; | |
CurrentMedia = new MediaFile(CurrentMedia.Title, CurrentMedia.Path, duration, CurrentMedia.ArtUrl, CurrentMedia.IsLocal); | |
} | |
}); | |
void PositionChanged(object sender, MediaPlayerPositionChangedEventArgs e) => | |
Task.Run(() => OnPositionChanged?.Invoke(e.Position)); | |
void TimeChanged(object sender, MediaPlayerTimeChangedEventArgs e) => | |
Task.Run(() => OnTimeChanged?.Invoke(TimeSpan.FromMilliseconds(e.Time))); | |
void OnMpMediaChanged(object sender, MediaPlayerMediaChangedEventArgs e) => | |
Task.Run(() => | |
{ | |
mp.LengthChanged -= LengthChanged; | |
mp.LengthChanged += LengthChanged; | |
OnMediaChanged?.Invoke(); | |
}); | |
public void Stop() | |
{ | |
//TODO Refactore this to the Dispose method | |
//mp.TimeChanged -= TimeChanged; | |
//mp.PositionChanged -= PositionChanged; | |
//mp.LengthChanged -= LengthChanged; | |
//mp.EndReached -= EndReached; | |
//mp.Playing -= Playing; | |
//mp.Paused -= Paused; | |
//mp.MediaChanged -= OnMpMediaChanged; | |
//isInit = false; | |
mp.Stop(); | |
mp.Media?.Dispose(); | |
mp.Media = null; | |
} | |
public void NextMusic() | |
{ | |
if (CurrentMedia == default) | |
return; | |
var index = MediaFiles.IndexOf(CurrentMedia); | |
index = index == -1 ? 0 : index; | |
if (!IsRepeateMode) | |
index = IsRandomMode | |
? random.Next(0, MediaFiles.Count - 1) | |
: ++index; | |
if (index > MediaFiles.Count) | |
index = MediaFiles.Count; | |
SetCurrentMusic(ref index); | |
} | |
MediaFile SetCurrentMusic(ref int index) | |
{ | |
var totalMedia = MediaFiles.Count; | |
if (totalMedia == 0) | |
return CurrentMedia; | |
if (index < 0) | |
index = totalMedia - 1; | |
index %= totalMedia; | |
var music = MediaFiles[index]; | |
SetSong(ref music); | |
return music; | |
} | |
public MediaFile PreviousMusic() | |
{ | |
if (CurrentMedia == default) | |
return default; | |
var index = MediaFiles.IndexOf(CurrentMedia); | |
index--; | |
return SetCurrentMusic(ref index); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment