From 6efd1529b8df0ee72db046c51bf3bf1e5e168d46 Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Wed, 3 May 2023 22:56:14 -0500 Subject: [PATCH] Finish PlayerInteropAudioService --- .../Playback/PlayerInteropAudioService.cs | 107 ++++++++++++++++-- .../Microsoft/Zune/Shell/ZuneApplication.cs | 4 +- .../TransportControls/TransportControls.lg.cs | 3 + .../TransportControls/TransportControls.oz.cs | 37 +++++- 4 files changed, 139 insertions(+), 12 deletions(-) diff --git a/ZuneImpl/Playback/PlayerInteropAudioService.cs b/ZuneImpl/Playback/PlayerInteropAudioService.cs index efd184c..7850471 100644 --- a/ZuneImpl/Playback/PlayerInteropAudioService.cs +++ b/ZuneImpl/Playback/PlayerInteropAudioService.cs @@ -1,8 +1,12 @@ #if OPENZUNE +using CommunityToolkit.Diagnostics; using MicrosoftZunePlayback; +using OwlCore.Storage; +using OwlCore.Storage.SystemIO; using StrixMusic.Sdk.MediaPlayback; using System; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -11,8 +15,33 @@ namespace Microsoft.Zune.Playback public class PlayerInteropAudioService : IAudioPlayerService { private readonly PlayerInterop _playbackWrapper = PlayerInterop.Instance; + private readonly IModifiableFolder _tempFolder; + private PlaybackItem m_currentSource; + private int _playbackId = 0; - public PlaybackItem CurrentSource { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public PlayerInteropAudioService(IModifiableFolder tempFolder) + { + _tempFolder = tempFolder; + } + + public PlayerInteropAudioService() + { + DirectoryInfo cacheFolderPath = new(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\Zune\OpenZune\PlayerInteropTemp")); + cacheFolderPath.Create(); + _tempFolder = new SystemFolder(cacheFolderPath); + } + + public static PlayerInteropAudioService Instance => new(); + + public PlaybackItem CurrentSource + { + get => m_currentSource; + set + { + m_currentSource = value; + CurrentSourceChanged?.Invoke(this, value); + } + } public TimeSpan Position => new(_playbackWrapper.Position); @@ -20,7 +49,7 @@ namespace Microsoft.Zune.Playback public double Volume => _playbackWrapper.Volume / 100d; - public double PlaybackSpeed => 1.0d; + public double PlaybackSpeed => _playbackWrapper.Rate; public event EventHandler CurrentSourceChanged; public event EventHandler QuantumProcessed; @@ -31,7 +60,8 @@ namespace Microsoft.Zune.Playback public Task ChangePlaybackSpeedAsync(double speed, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + _playbackWrapper.Rate = (float)speed; + return Task.CompletedTask; } public Task ChangeVolumeAsync(double volume, CancellationToken cancellationToken = default) @@ -46,15 +76,29 @@ namespace Microsoft.Zune.Playback return Task.CompletedTask; } - public Task Play(PlaybackItem sourceConfig, CancellationToken cancellationToken = default) + public async Task Play(PlaybackItem sourceConfig, CancellationToken cancellationToken = default) { - _playbackWrapper.Play(); - return Task.CompletedTask; + var media = await LoadAsync(sourceConfig.MediaConfig); + + Iris.Application.DeferredInvoke(new Iris.DeferredInvokeHandler(delegate + { + // Play cannot be called immediately after SetUri, + // so we have to listen for when the change is done. + _playbackWrapper.UriSet += PlaybackWrapper_UriSet; + _playbackWrapper.SetUri(media.Uri, 0, media.Id); + }), Iris.DeferredInvokePriority.Normal); } - public Task Preload(PlaybackItem sourceConfig, CancellationToken cancellationToken = default) + private void PlaybackWrapper_UriSet(object sender, EventArgs e) { - throw new NotImplementedException(); + _playbackWrapper.UriSet -= PlaybackWrapper_UriSet; + _playbackWrapper.Play(); + } + + public async Task Preload(PlaybackItem sourceConfig, CancellationToken cancellationToken = default) + { + var media = await LoadAsync(sourceConfig.MediaConfig); + _playbackWrapper.SetNextUri(media.Uri, 0, media.Id); } public Task ResumeAsync(CancellationToken cancellationToken = default) @@ -69,6 +113,53 @@ namespace Microsoft.Zune.Playback _playbackWrapper.SeekToAbsolutePosition(position.Ticks); return Task.CompletedTask; } + + private async Task LoadAsync(IMediaSourceConfig mediaConfig, bool saveStream = true) + { + Guard.IsNotNull(mediaConfig); + + mediaConfig.FileStreamSource?.Dispose(); + + var url = mediaConfig.MediaSourceUri?.ToString(); + if (url is null) + { + if (IsValidUri(mediaConfig.Id)) + { + url = mediaConfig.Id; + } + else if (saveStream && mediaConfig.FileStreamSource is not null) + { + var dstFile = await _tempFolder.CreateFileAsync(mediaConfig.Id, true); + using (var stream = await dstFile.OpenStreamAsync(FileAccess.ReadWrite)) + { + await mediaConfig.FileStreamSource.CopyToAsync(stream); + } + + Guard.IsTrue(IsValidUri(url)); + url = dstFile.Id; + } + else + { + throw new ArgumentException("PlayerInterop can only load media from a URI."); + } + } + + return new(url, ++_playbackId); + } + + private static bool IsValidUri(string? url) => Uri.TryCreate(url, UriKind.Absolute, out _); + + private readonly struct PlayerInteropMedia + { + public PlayerInteropMedia(string uri, int id) + { + Uri = uri; + Id = id; + } + + public string Uri { get; } + public int Id { get; } + } } } diff --git a/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs b/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs index 6eeb07c..78ef883 100644 --- a/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs +++ b/ZuneShell/Microsoft/Zune/Shell/ZuneApplication.cs @@ -182,9 +182,9 @@ namespace Microsoft.Zune.Shell // Perform any async initialization needed. Authenticating, connecting to database, etc. // Add plugins - string mfAudioServiceId = Guid.NewGuid().ToString(); PlaybackHandler = new PlaybackHandlerService(); PlaybackHandler.RegisterAudioPlayer(VlcAudioService.Instance, localCore.InstanceId); + //PlaybackHandler.RegisterAudioPlayer(PlayerInteropAudioService.Instance, localCore.InstanceId); DataRoot = new StrixDataRootPluginWrapper(mergedLayer, new PlaybackHandlerPlugin(PlaybackHandler) @@ -219,7 +219,7 @@ namespace Microsoft.Zune.Shell ITrack firstTrack = addedItems[1].Data; - await firstTrack.PlayArtistCollectionAsync(); + //await firstTrack.PlayArtistCollectionAsync(); } } diff --git a/ZuneShell/ZuneUI/TransportControls/TransportControls.lg.cs b/ZuneShell/ZuneUI/TransportControls/TransportControls.lg.cs index 7f9bf35..8dc13f1 100644 --- a/ZuneShell/ZuneUI/TransportControls/TransportControls.lg.cs +++ b/ZuneShell/ZuneUI/TransportControls/TransportControls.lg.cs @@ -6,6 +6,9 @@ namespace ZuneUI { partial class TransportControls { + private void OnInit() { } + private void OnUninit() { } + private void OnPlayClicked(object sender, EventArgs e) { if (!this._play.Available) diff --git a/ZuneShell/ZuneUI/TransportControls/TransportControls.oz.cs b/ZuneShell/ZuneUI/TransportControls/TransportControls.oz.cs index 31b570d..d7fd3d3 100644 --- a/ZuneShell/ZuneUI/TransportControls/TransportControls.oz.cs +++ b/ZuneShell/ZuneUI/TransportControls/TransportControls.oz.cs @@ -1,4 +1,6 @@ -using Microsoft.Zune.Util; +using Microsoft.Iris; +using Microsoft.Zune.Shell; +using Microsoft.Zune.Util; using MicrosoftZunePlayback; using StrixMusic.Sdk.MediaPlayback; using System; @@ -7,7 +9,18 @@ namespace ZuneUI { partial class TransportControls { - private readonly IPlaybackHandlerService _playbackHandler = Microsoft.Zune.Shell.ZuneApplication.PlaybackHandler; + private IPlaybackHandlerService _playbackHandler; + + private void OnInit() + { + _playbackHandler = ZuneApplication.PlaybackHandler; + _playbackHandler.CurrentItemChanged += PlaybackHandler_CurrentItemChanged; + } + + private void OnUninit() + { + _playbackHandler.CurrentItemChanged -= PlaybackHandler_CurrentItemChanged; + } private async void OnPlayClicked(object sender, EventArgs e) { @@ -131,5 +144,25 @@ namespace ZuneUI } } } + + private async void PlaybackHandler_CurrentItemChanged(object sender, PlaybackItem e) + { + // NOTE: This currently does not work. Perhaps something to do with + // the lack of a valid media ID? + + string title = $"[Strix] {e.Track.Name}"; + int num = e.Track.TrackNumber ?? 0; + string album = e.Track.Album?.Name ?? string.Empty; + + string artist = string.Empty; + await foreach (var artistItem in e.Track.GetArtistItemsAsync(1, 0)) + artist = artistItem.Name; + + Application.DeferredInvoke(new DeferredInvokeHandler(delegate + { + Microsoft.Zune.Util.Notification.ResetNowPlaying(); + Microsoft.Zune.Util.Notification.BroadcastNowPlaying(EMediaTypes.eMediaTypeAudio, album, artist, title, num, Guid.NewGuid()); + }), DeferredInvokePriority.Normal); + } } }