mirror of
https://github.com/ZuneDev/ZuneShell.dll.git
synced 2026-07-27 13:11:51 -07:00
2080 lines
81 KiB
C#
2080 lines
81 KiB
C#
// Decompiled with JetBrains decompiler
|
|
// Type: ZuneUI.TransportControls
|
|
// Assembly: ZuneShell, Version=4.7.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
|
|
// MVID: FC8028F3-A47B-4FB4-B35B-11D1752D8264
|
|
// Assembly location: C:\Program Files\Zune\ZuneShell.dll
|
|
|
|
using Microsoft.Iris;
|
|
using Microsoft.Zune;
|
|
using Microsoft.Zune.Configuration;
|
|
using Microsoft.Zune.PerfTrace;
|
|
using Microsoft.Zune.Playback;
|
|
using Microsoft.Zune.QuickMix;
|
|
using Microsoft.Zune.Service;
|
|
using Microsoft.Zune.Util;
|
|
using MicrosoftZuneLibrary;
|
|
using MicrosoftZunePlayback;
|
|
using StrixMusic.Sdk.MediaPlayback;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Threading;
|
|
using UIXControls;
|
|
|
|
namespace ZuneUI
|
|
{
|
|
public partial class TransportControls : SingletonModelItem<TransportControls>
|
|
{
|
|
internal const long c_TicksPerSecond = 10000000;
|
|
private const long _rewindDelay = 50000000;
|
|
private const float c_overscanFactor = 0.1f;
|
|
private const int c_maxConsecutiveErrors = 5;
|
|
private const int c_ratingUnrated = -1;
|
|
private const string c_knownInvalidUri = ".:* INVALID URI *:.";
|
|
private IAudioPlayerService _player;
|
|
private BooleanChoice _shuffling;
|
|
private BooleanChoice _repeating;
|
|
private BooleanChoice _muted;
|
|
private BooleanChoice _showTotalTime;
|
|
private BooleanChoice _showNowPlayingList;
|
|
private BooleanChoice _fastforwarding;
|
|
private BooleanChoice _rewinding;
|
|
private RangedValue _volume;
|
|
private Command _play;
|
|
private Command _pause;
|
|
private Command _back;
|
|
private Command _forward;
|
|
private Command _stop;
|
|
private bool _playingVideo;
|
|
private bool _opening;
|
|
private bool _buffering;
|
|
private bool _seekEnabled;
|
|
private int _zoomScaleFactor;
|
|
private Command _fastforwardhotkey;
|
|
private Command _rewindhotkey;
|
|
private NowPlayingList _playlistPending;
|
|
private NowPlayingList _playlistCurrent;
|
|
private Microsoft.Iris.Timer _timerDelayedConfigPersist;
|
|
private VideoStream _videoStream;
|
|
private PlaybackState _lastKnownPlayerState;
|
|
private PlaybackState _lastKnownTransportState;
|
|
private long _lastKnownPosition;
|
|
private Notification _nowPlayingNotification;
|
|
private float _currentTrackDuration;
|
|
private float _currentTrackPosition;
|
|
private float _downloadProgress;
|
|
private PlaybackTrack _lastKnownPreparedTrack;
|
|
private PlaybackTrack _lastKnownPlaybackTrack;
|
|
private List<PlaybackTrack> _tracksSubmittedToPlayer = new(2);
|
|
private Dictionary<PlaybackTrack, int> _errors = new();
|
|
private int _consecutiveErrors;
|
|
private bool _showErrors = true;
|
|
private int _lastKnownSetUriCallID;
|
|
private static string _savedNowPlayingFilename = Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Shell.LoadString(StringId.IDS_APPDATAFOLDERNAME).TrimStart('\\')), "NowPlaying.dat");
|
|
private Shell _shellInstance;
|
|
private PlayerState _playerState;
|
|
private bool _streamingReportIsOpen;
|
|
private Guid _streamingReportMediaId;
|
|
private Guid _streamingReportMediaInstanceId;
|
|
private TaskbarPlayer _taskbarPlayer;
|
|
private bool _isInitialized;
|
|
private bool _resumeLastNowPlayingRequested;
|
|
private bool _shuffleAllRequested;
|
|
private JumpListPin _requestedJumpListPin;
|
|
private PlaybackContext _pagePlaybackContext;
|
|
private PlaybackTrack _currentTrack;
|
|
private bool _isPlaying;
|
|
private bool _hasPlaylist;
|
|
private bool _playlistSupportsShuffle = true;
|
|
private bool _hasPlayed;
|
|
private int _currentTrackIndex = -1;
|
|
private ArrayListDataSet _currentPlaylist;
|
|
private bool _isContextCompatible;
|
|
private bool _isStreamingVideo;
|
|
private bool _supressDownloads;
|
|
private DateTime _currentPlayStartTime = DateTime.MinValue;
|
|
private List<SpectrumOutputConfig> _spectrumConfigList;
|
|
private bool _isSpectrumAvailable;
|
|
private int _lastKnownCurrentTrackRating;
|
|
private EventHandler _currentTrackRatingChangedEventHandler;
|
|
private Microsoft.Iris.Timer _isStreamingTimeoutTimer;
|
|
private bool _dontPlayMarketplaceTracks;
|
|
private int _bandwidthCapacity;
|
|
private BandwidthUpdateArgs _bandwidthUpdateInfo;
|
|
|
|
public TransportControls()
|
|
{
|
|
_player = PlayerInteropAudioService.Instance;
|
|
_taskbarPlayer = TaskbarPlayer.Instance;
|
|
_videoStream = new VideoStream();
|
|
if (!CanRender3DVideo)
|
|
_videoStream.DisplayDetailsChanged += OnVideoDetailsChanged;
|
|
_shuffling = new BooleanChoice(this);
|
|
_shuffling.Value = ClientConfiguration.Playback.ModeShuffle;
|
|
_shuffling.ChosenChanged += OnShufflingChanged;
|
|
UpdateShufflingDescription();
|
|
_repeating = new BooleanChoice(this);
|
|
_repeating.Value = ClientConfiguration.Playback.ModeLoop;
|
|
_repeating.ChosenChanged += OnRepeatingChanged;
|
|
UpdateRepeatingDescription();
|
|
_muted = new BooleanChoice(this);
|
|
_muted.Value = ClientConfiguration.Playback.Mute;
|
|
_muted.ChosenChanged += OnMutingChanged;
|
|
UpdateMutingDescription();
|
|
_showTotalTime = new BooleanChoice(this);
|
|
_showTotalTime.Value = ClientConfiguration.Playback.ShowTotalTime;
|
|
_showTotalTime.ChosenChanged += OnShowTotalTimeChanged;
|
|
_showNowPlayingList = new BooleanChoice(this);
|
|
_showNowPlayingList.Value = ClientConfiguration.Playback.ShowNowPlayingList;
|
|
_showNowPlayingList.ChosenChanged += OnShowNowPlayingListChanged;
|
|
UpdateShowNowPlayingListDescription();
|
|
_fastforwarding = new BooleanChoice(this);
|
|
_fastforwarding.ChosenChanged += OnFastforwardingChanged;
|
|
_rewinding = new BooleanChoice(this);
|
|
_rewinding.ChosenChanged += OnRewindingChanged;
|
|
float num = ClientConfiguration.Playback.Volume;
|
|
if (num < 0.0 || num > 100.0)
|
|
num = 50f;
|
|
_volume = new RangedValue(this);
|
|
_volume.MinValue = 0.0f;
|
|
_volume.MaxValue = 100f;
|
|
_volume.Value = num;
|
|
_volume.PropertyChanged += OnVolumeControlChanged;
|
|
_play = new Command(this, Shell.LoadString(StringId.IDS_PLAY), OnPlayClicked);
|
|
_play.Available = false;
|
|
_pause = new Command(this, Shell.LoadString(StringId.IDS_PAUSE), OnPauseClicked);
|
|
_pause.Available = false;
|
|
_back = new Command(this, Shell.LoadString(StringId.IDS_PREVIOUS), OnBackClicked);
|
|
_back.Available = false;
|
|
_forward = new Command(this, Shell.LoadString(StringId.IDS_NEXT), OnForwardClicked);
|
|
_forward.Available = false;
|
|
_stop = new Command(this, Shell.LoadString(StringId.IDS_STOP), OnStopClicked);
|
|
_stop.Available = false;
|
|
_fastforwardhotkey = new Command(this, new EventHandler(OnFastforwardHotkeyPressed));
|
|
_rewindhotkey = new Command(this, new EventHandler(OnRewindHotkeyPressed));
|
|
_player.CurrentSourceChanged += Player_CurrentItemChanged;
|
|
_player.PlaybackStateChanged += OnPlaybackStatusChanged;
|
|
_player.PositionChanged += OnTransportPositionChanged;
|
|
// TODO: _player.VolumeChanged += OnVolumeControlChanged;
|
|
_lastKnownPlayerState = _player.PlaybackState;
|
|
_lastKnownTransportState = _player.PlaybackState;
|
|
_lastKnownPosition = _player.Position.Ticks;
|
|
_shellInstance = (Shell)ZuneShell.DefaultInstance;
|
|
_shellInstance.PropertyChanged += OnShellPropertyChanged;
|
|
_timerDelayedConfigPersist = new Microsoft.Iris.Timer();
|
|
_timerDelayedConfigPersist.Interval = 500;
|
|
_timerDelayedConfigPersist.AutoRepeat = false;
|
|
_timerDelayedConfigPersist.Tick += OnDelayedConfigPersistTimerTick;
|
|
_playerState = PlayerState.Stopped;
|
|
_spectrumConfigList = new List<SpectrumOutputConfig>();
|
|
_isSpectrumAvailable = false;
|
|
// TODO: Handle audio services that can't seek
|
|
IsSeekEnabled = true;// _playbackWrapper.CanSeek;
|
|
Download.Instance.DownloadProgressEvent += OnDownloadProgressed;
|
|
_lastKnownCurrentTrackRating = -1;
|
|
_currentTrackRatingChangedEventHandler = OnCurrentTrackRatingChanged;
|
|
_isStreamingTimeoutTimer = new Microsoft.Iris.Timer(this);
|
|
_isStreamingTimeoutTimer.Interval = 30000;
|
|
_isStreamingTimeoutTimer.AutoRepeat = false;
|
|
_isStreamingTimeoutTimer.Tick += OnIsStreamingTimeout;
|
|
SignIn.Instance.SignInStatusUpdatedEvent += OnSignInEvent;
|
|
}
|
|
|
|
private bool TryGetPlayerInterop(out PlayerInterop playerInterop)
|
|
{
|
|
if (_player is PlayerInteropAudioService pias)
|
|
{
|
|
playerInterop = pias.PlaybackWrapper;
|
|
return true;
|
|
}
|
|
|
|
playerInterop = null;
|
|
return false;
|
|
}
|
|
|
|
protected override void OnDispose(bool fDisposing)
|
|
{
|
|
base.OnDispose(fDisposing);
|
|
if (!fDisposing)
|
|
return;
|
|
DisconnectAllSpectrumAnimationSources();
|
|
_isSpectrumAvailable = false;
|
|
Microsoft.Zune.Util.Notification.ResetNowPlaying();
|
|
if (WillSaveCurrentPlaylistOnShutdown())
|
|
{
|
|
try
|
|
{
|
|
using (Stream serializationStream = File.Create(_savedNowPlayingFilename))
|
|
new BinaryFormatter().Serialize(serializationStream, _playlistCurrent);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
else if (File.Exists(_savedNowPlayingFilename))
|
|
{
|
|
try
|
|
{
|
|
File.Delete(_savedNowPlayingFilename);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
}
|
|
}
|
|
_lastKnownPlaybackTrack?.OnEndPlayback(false);
|
|
_player.CurrentSourceChanged -= Player_CurrentItemChanged;
|
|
_player.PlaybackStateChanged -= OnPlaybackStatusChanged;
|
|
_player.PositionChanged -= OnTransportPositionChanged;
|
|
// TODO: _player.VolumeChanged -= OnVolumeControlChanged;
|
|
if (_videoStream != null && !CanRender3DVideo)
|
|
_videoStream.DisplayDetailsChanged -= OnVideoDetailsChanged;
|
|
PlayerInterop.Instance.Dispose();
|
|
if (_videoStream != null)
|
|
{
|
|
_videoStream.Dispose();
|
|
_videoStream = null;
|
|
}
|
|
if (_timerDelayedConfigPersist != null)
|
|
{
|
|
_timerDelayedConfigPersist.Tick -= OnDelayedConfigPersistTimerTick;
|
|
_timerDelayedConfigPersist.Dispose();
|
|
_timerDelayedConfigPersist = null;
|
|
}
|
|
_shellInstance.PropertyChanged -= OnShellPropertyChanged;
|
|
Download.Instance.DownloadProgressEvent -= OnDownloadProgressed;
|
|
if (_currentTrack != null)
|
|
_currentTrack.RatingChanged.Invoked -= _currentTrackRatingChangedEventHandler;
|
|
SignIn.Instance.SignInStatusUpdatedEvent -= OnSignInEvent;
|
|
}
|
|
|
|
private void OnSignInEvent(object sender, EventArgs args)
|
|
{
|
|
_playlistCurrent?.UpdateTracks();
|
|
_playlistPending?.UpdateTracks();
|
|
}
|
|
|
|
private void PersistSettings()
|
|
{
|
|
if (_timerDelayedConfigPersist == null)
|
|
return;
|
|
|
|
_timerDelayedConfigPersist.Stop();
|
|
_timerDelayedConfigPersist.Start();
|
|
}
|
|
|
|
private void OnDelayedConfigPersistTimerTick(object sender, EventArgs args)
|
|
{
|
|
ClientConfiguration.Playback.ModeShuffle = _shuffling.Value;
|
|
ClientConfiguration.Playback.ModeLoop = _repeating.Value;
|
|
ClientConfiguration.Playback.Mute = _muted.Value;
|
|
ClientConfiguration.Playback.Volume = (int)_volume.Value;
|
|
ClientConfiguration.Playback.ShowTotalTime = _showTotalTime.Value;
|
|
ClientConfiguration.Playback.ShowNowPlayingList = _showNowPlayingList.Value;
|
|
}
|
|
|
|
private void OnCurrentTrackRatingChanged(object sender, EventArgs args)
|
|
{
|
|
int num = -1;
|
|
if (CurrentTrack != null && CurrentTrack.CanRate)
|
|
num = CurrentTrackRating;
|
|
if (num == _lastKnownCurrentTrackRating)
|
|
return;
|
|
FirePropertyChanged("CurrentTrackRating");
|
|
_lastKnownCurrentTrackRating = num;
|
|
}
|
|
|
|
public void TrackRatingUpdatedExternally(int mediaID, int newRating)
|
|
{
|
|
if (CurrentPlaylist == null)
|
|
return;
|
|
|
|
foreach (PlaybackTrack playbackTrack in CurrentPlaylist)
|
|
{
|
|
if (playbackTrack is LibraryPlaybackTrack libraryPlaybackTrack && libraryPlaybackTrack.MediaId == mediaID)
|
|
libraryPlaybackTrack.RatingUpdatedExternally(newRating);
|
|
}
|
|
}
|
|
|
|
private void OnIsStreamingTimeout(object sender, EventArgs args) => SupressDownloads = false;
|
|
|
|
private void DeserializeNowPlayingList(object arg)
|
|
{
|
|
object args = null;
|
|
if (arg is string path)
|
|
{
|
|
if (File.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
using (Stream serializationStream = File.OpenRead(path))
|
|
args = new BinaryFormatter().Deserialize(serializationStream);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
if (args != null)
|
|
Application.DeferredInvoke(new DeferredInvokeHandler(DeserializationComplete), args);
|
|
}
|
|
|
|
private void DeserializationComplete(object arg)
|
|
{
|
|
if (_playlistCurrent != null || arg is not NowPlayingList nowPlayingList)
|
|
return;
|
|
_playlistCurrent = nowPlayingList;
|
|
ShowNotification();
|
|
UpdatePropertiesAndCommands();
|
|
if (_resumeLastNowPlayingRequested)
|
|
Play.Invoke();
|
|
}
|
|
|
|
public bool IsInitialized
|
|
{
|
|
get => _isInitialized;
|
|
private set
|
|
{
|
|
if (_isInitialized == value)
|
|
return;
|
|
_isInitialized = value;
|
|
FirePropertyChanged(nameof(IsInitialized));
|
|
}
|
|
}
|
|
|
|
public bool HasPlayed
|
|
{
|
|
get => _hasPlayed;
|
|
private set
|
|
{
|
|
if (_hasPlayed == value)
|
|
return;
|
|
_hasPlayed = value;
|
|
FirePropertyChanged(nameof(HasPlayed));
|
|
}
|
|
}
|
|
|
|
public event EventHandler PlaybackStopped;
|
|
|
|
public BooleanChoice Shuffling => _shuffling;
|
|
|
|
public BooleanChoice Repeating => _repeating;
|
|
|
|
public BooleanChoice Muted => _muted;
|
|
|
|
public BooleanChoice ShowTotalTime => _showTotalTime;
|
|
|
|
public BooleanChoice ShowNowPlayingList => _showNowPlayingList;
|
|
|
|
public BooleanChoice Fastforwarding => _fastforwarding;
|
|
|
|
public BooleanChoice Rewinding => _rewinding;
|
|
|
|
public RangedValue Volume => _volume;
|
|
|
|
public Command Play => _play;
|
|
|
|
public Command Pause => _pause;
|
|
|
|
public Command Stop => _stop;
|
|
|
|
public Command Back => _back;
|
|
|
|
public Command Forward => _forward;
|
|
|
|
public bool Opening
|
|
{
|
|
get => _opening;
|
|
private set
|
|
{
|
|
if (_opening == value)
|
|
return;
|
|
_opening = value;
|
|
FirePropertyChanged(nameof(Opening));
|
|
}
|
|
}
|
|
|
|
public bool Buffering
|
|
{
|
|
get => _buffering;
|
|
private set
|
|
{
|
|
if (_buffering == value)
|
|
return;
|
|
_buffering = value;
|
|
FirePropertyChanged(nameof(Buffering));
|
|
}
|
|
}
|
|
|
|
public bool IsSeekEnabled
|
|
{
|
|
get => _seekEnabled;
|
|
private set
|
|
{
|
|
if (_seekEnabled == value)
|
|
return;
|
|
_seekEnabled = value;
|
|
FirePropertyChanged(nameof(IsSeekEnabled));
|
|
}
|
|
}
|
|
|
|
public bool IsStreamingVideo
|
|
{
|
|
get => _isStreamingVideo;
|
|
private set
|
|
{
|
|
if (_isStreamingVideo == value)
|
|
return;
|
|
_isStreamingVideo = value;
|
|
FirePropertyChanged(nameof(IsStreamingVideo));
|
|
}
|
|
}
|
|
|
|
public bool SupressDownloads
|
|
{
|
|
get => _supressDownloads;
|
|
private set
|
|
{
|
|
if (_supressDownloads == value)
|
|
return;
|
|
_supressDownloads = value;
|
|
FirePropertyChanged(nameof(SupressDownloads));
|
|
}
|
|
}
|
|
|
|
public int ZoomScaleFactor
|
|
{
|
|
get => _zoomScaleFactor;
|
|
set
|
|
{
|
|
_zoomScaleFactor = value;
|
|
FirePropertyChanged(nameof(ZoomScaleFactor));
|
|
}
|
|
}
|
|
|
|
public bool Playing => _isPlaying;
|
|
|
|
public bool PlayingVideo
|
|
{
|
|
get => _playingVideo;
|
|
private set
|
|
{
|
|
if (_playingVideo == value)
|
|
return;
|
|
_playingVideo = value;
|
|
FirePropertyChanged(nameof(PlayingVideo));
|
|
}
|
|
}
|
|
|
|
public Command FastforwardHotkey => _fastforwardhotkey;
|
|
|
|
public Command RewindHotkey => _rewindhotkey;
|
|
|
|
public int CurrentTrackIndex => _currentTrackIndex;
|
|
|
|
public bool WillSaveCurrentPlaylistOnShutdown() => _playlistCurrent != null && _playlistCurrent.CurrentTrack != null && !_playlistCurrent.CurrentTrack.IsVideo && _playlistCurrent.QuickMixSession == null;
|
|
|
|
public void StartPlayingAt(PlaybackTrack track)
|
|
{
|
|
if (_playlistCurrent == null || _playlistCurrent.TrackList == null)
|
|
return;
|
|
int newCurrentIndex = _playlistCurrent.TrackList.IndexOf(track);
|
|
if (newCurrentIndex <= -1)
|
|
return;
|
|
StartPlayingAt(newCurrentIndex);
|
|
}
|
|
|
|
public async void StartPlayingAt(int newCurrentIndex)
|
|
{
|
|
if (_playlistCurrent == null)
|
|
return;
|
|
|
|
_playlistCurrent.MoveToTrackIndex(newCurrentIndex);
|
|
if (_playerState == PlayerState.Playing)
|
|
{
|
|
SetUriOnPlayer();
|
|
}
|
|
else
|
|
{
|
|
_playlistPending = _playlistCurrent;
|
|
if (_playerState == PlayerState.Paused)
|
|
await _player.StopAsync();
|
|
else
|
|
PlayPendingList();
|
|
}
|
|
}
|
|
|
|
public void CloseCurrentSession() => Stop.Invoke();
|
|
|
|
public void SeekToPosition(float value)
|
|
{
|
|
var position = TimeSpan.FromTicks((long)(value * 10000000.0));
|
|
AsyncHelper.Run(_player?.SeekAsync(position));
|
|
_rewinding.Value = false;
|
|
_fastforwarding.Value = false;
|
|
}
|
|
|
|
public float CurrentTrackDuration
|
|
{
|
|
get => _currentTrackDuration;
|
|
private set
|
|
{
|
|
if (_currentTrackDuration == (double)value)
|
|
return;
|
|
_currentTrackDuration = value;
|
|
FirePropertyChanged(nameof(CurrentTrackDuration));
|
|
}
|
|
}
|
|
|
|
public float CurrentTrackPosition
|
|
{
|
|
get => _currentTrackPosition;
|
|
private set
|
|
{
|
|
if (_currentTrackPosition == (double)value)
|
|
return;
|
|
_currentTrackPosition = value;
|
|
FirePropertyChanged(nameof(CurrentTrackPosition));
|
|
}
|
|
}
|
|
|
|
public float CurrentTrackDownloadProgress
|
|
{
|
|
get => _downloadProgress;
|
|
private set
|
|
{
|
|
if (_downloadProgress == (double)value)
|
|
return;
|
|
_downloadProgress = value;
|
|
FirePropertyChanged(nameof(CurrentTrackDownloadProgress));
|
|
}
|
|
}
|
|
|
|
public PlaybackTrack CurrentTrack => _currentTrack;
|
|
|
|
public int CurrentTrackRating => CurrentTrack == null || !CurrentTrack.CanRate ? 0 : CurrentTrack.UserRating;
|
|
|
|
public bool ShowErrors
|
|
{
|
|
get => _showErrors;
|
|
set
|
|
{
|
|
if (_showErrors == value)
|
|
return;
|
|
_showErrors = value;
|
|
FirePropertyChanged(nameof(ShowErrors));
|
|
}
|
|
}
|
|
|
|
public void ClearAllErrors()
|
|
{
|
|
if (_errors.Count <= 0)
|
|
return;
|
|
_errors.Clear();
|
|
FirePropertyChanged("ErrorCount");
|
|
}
|
|
|
|
public bool IsCurrentTrack(Guid zuneMediaId)
|
|
{
|
|
PlaybackTrack currentTrack = CurrentTrack;
|
|
return currentTrack != null && !GuidHelper.IsEmpty(currentTrack.ZuneMediaId) && currentTrack.ZuneMediaId == zuneMediaId;
|
|
}
|
|
|
|
public int GetErrorCode(Guid zuneMediaId)
|
|
{
|
|
if (_errors.Count > 0)
|
|
{
|
|
foreach (KeyValuePair<PlaybackTrack, int> error in _errors)
|
|
{
|
|
PlaybackTrack key = error.Key;
|
|
if (key != null && key.ZuneMediaId == zuneMediaId)
|
|
return error.Value;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public bool IsCurrentTrack(int id, MediaType type, Guid zuneMediaId)
|
|
{
|
|
if (CurrentTrack is not LibraryPlaybackTrack currentTrack)
|
|
return IsCurrentTrack(zuneMediaId);
|
|
return currentTrack.MediaId == id && currentTrack.MediaType == type;
|
|
}
|
|
|
|
public int GetLibraryErrorCode(int id, MediaType type)
|
|
{
|
|
if (_errors.Count > 0)
|
|
{
|
|
foreach (KeyValuePair<PlaybackTrack, int> error in _errors)
|
|
{
|
|
if (error.Key is LibraryPlaybackTrack key && key.MediaId == id && key.MediaType == type)
|
|
return error.Value;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int GetLibraryErrorCode(PlaybackTrack track) => _errors.TryGetValue(track, out int num) ? num : 0;
|
|
|
|
internal void ClearError(PlaybackTrack track)
|
|
{
|
|
if (_errors.Remove(track))
|
|
FirePropertyChanged("ErrorCount");
|
|
}
|
|
|
|
public int ErrorCount => _errors.Count;
|
|
|
|
public VideoStream VideoStream => _videoStream;
|
|
|
|
public bool CanRender3DVideo => Application.RenderingType != RenderingType.GDI;
|
|
|
|
public bool HasPlaylist => _hasPlaylist;
|
|
|
|
public bool PlaylistSupportsShuffle => _playlistSupportsShuffle;
|
|
|
|
public ArrayListDataSet CurrentPlaylist => _currentPlaylist;
|
|
|
|
public string QuickMixTitle
|
|
{
|
|
get
|
|
{
|
|
string str = string.Empty;
|
|
if (_playlistCurrent != null)
|
|
str = _playlistCurrent.QuickMixTitle;
|
|
return str;
|
|
}
|
|
}
|
|
|
|
public EQuickMixType QuickMixType
|
|
{
|
|
get
|
|
{
|
|
EQuickMixType equickMixType = EQuickMixType.eQuickMixTypeInvalid;
|
|
if (_playlistCurrent != null)
|
|
equickMixType = _playlistCurrent.QuickMixType;
|
|
return equickMixType;
|
|
}
|
|
}
|
|
|
|
public bool DontPlayMarketplaceTracks
|
|
{
|
|
get => _dontPlayMarketplaceTracks;
|
|
set
|
|
{
|
|
if (value == _dontPlayMarketplaceTracks)
|
|
return;
|
|
_dontPlayMarketplaceTracks = value;
|
|
if (_playlistCurrent != null)
|
|
_playlistCurrent.DontPlayMarketplaceTracks = _dontPlayMarketplaceTracks;
|
|
if (_playlistPending == null)
|
|
return;
|
|
_playlistPending.DontPlayMarketplaceTracks = _dontPlayMarketplaceTracks;
|
|
}
|
|
}
|
|
|
|
public QuickMixSession QuickMixSession
|
|
{
|
|
get
|
|
{
|
|
QuickMixSession quickMixSession = null;
|
|
if (_playlistCurrent != null)
|
|
quickMixSession = _playlistCurrent.QuickMixSession;
|
|
return quickMixSession;
|
|
}
|
|
}
|
|
|
|
public bool IsPlaybackContextCompatible => _isContextCompatible;
|
|
|
|
public JumpListPin RequestedJumpListPin
|
|
{
|
|
get => !IsInitialized ? null : _requestedJumpListPin;
|
|
set
|
|
{
|
|
if (_requestedJumpListPin == value)
|
|
return;
|
|
_requestedJumpListPin = value;
|
|
if (!IsInitialized)
|
|
return;
|
|
FirePropertyChanged(nameof(RequestedJumpListPin));
|
|
}
|
|
}
|
|
|
|
public bool ShuffleAllRequested
|
|
{
|
|
get => IsInitialized && _shuffleAllRequested;
|
|
set
|
|
{
|
|
if (_shuffleAllRequested == value)
|
|
return;
|
|
_shuffleAllRequested = value;
|
|
if (!IsInitialized)
|
|
return;
|
|
FirePropertyChanged(nameof(ShuffleAllRequested));
|
|
}
|
|
}
|
|
|
|
public int BandwidthCapacity
|
|
{
|
|
get => _bandwidthCapacity;
|
|
private set
|
|
{
|
|
_bandwidthCapacity = value;
|
|
FirePropertyChanged(nameof(BandwidthCapacity));
|
|
}
|
|
}
|
|
|
|
public BandwidthUpdateArgs BandwidthUpdateInfo
|
|
{
|
|
get => _bandwidthUpdateInfo;
|
|
private set
|
|
{
|
|
_bandwidthUpdateInfo = value;
|
|
FirePropertyChanged(nameof(BandwidthUpdateInfo));
|
|
}
|
|
}
|
|
|
|
private void OnBandwidthCapacityUpdate(object sender, BandwidthUpdateArgs args) => Application.DeferredInvoke(new DeferredInvokeHandler(OnBandwidthCapacityUpdateOnApp), args);
|
|
|
|
private void OnBandwidthCapacityUpdateOnApp(object obj)
|
|
{
|
|
if (obj == null)
|
|
return;
|
|
BandwidthUpdateArgs bandwidthUpdateArgs = (BandwidthUpdateArgs)obj;
|
|
if (bandwidthUpdateArgs == null || bandwidthUpdateArgs.currentState != MBRHeuristicState.Playback)
|
|
return;
|
|
BandwidthCapacity = bandwidthUpdateArgs.RecentAverageBandwidth;
|
|
BandwidthUpdateInfo = bandwidthUpdateArgs;
|
|
}
|
|
|
|
private void OnStreamingRestrictionResponse(HRESULT hr)
|
|
{
|
|
if (!hr.IsError)
|
|
return;
|
|
string title = Shell.LoadString(StringId.IDS_PLAYBACK_CANNOT_PLAY);
|
|
string description;
|
|
if (hr == HRESULT._ZEST_E_MAX_CONCURRENTSTREAMING_EXCEEDED || hr == HRESULT._ZEST_E_MULTITUNER_CONCURRENTSTREAMING_DETECTED || hr == HRESULT._ZEST_E_MEDIAINSTANCE_STREAMING_OCCUPIED)
|
|
description = Microsoft.Zune.ErrorMapperApi.ErrorMapperApi.GetMappedErrorDescriptionAndUrl(hr.Int).Description;
|
|
else
|
|
description = Shell.LoadString(StringId.IDS_PLAYBACK_UNKNOWN_CONCURRENT_STREAMING_RESTRICTION);
|
|
Application.DeferredInvoke(delegate
|
|
{
|
|
Stop.Invoke();
|
|
MessageBox.Show(title, description, null);
|
|
}, DeferredInvokePriority.Low);
|
|
}
|
|
|
|
private void ReportStreamingAction(PlayerState previousPlayerState)
|
|
{
|
|
if (CurrentTrack != null && CurrentTrack.IsVideo && CurrentTrack.IsStreaming)
|
|
{
|
|
Guid zuneMediaId = CurrentTrack.ZuneMediaId;
|
|
if (zuneMediaId != _streamingReportMediaId && _streamingReportIsOpen)
|
|
{
|
|
_streamingReportIsOpen = false;
|
|
previousPlayerState = PlayerState.Stopped;
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Stop, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
}
|
|
if (previousPlayerState == PlayerState.Stopped && _playerState == PlayerState.Playing)
|
|
{
|
|
_streamingReportMediaInstanceId = CurrentTrack.ZuneMediaInstanceId;
|
|
if (!(_streamingReportMediaInstanceId != Guid.Empty))
|
|
return;
|
|
_streamingReportIsOpen = true;
|
|
_streamingReportMediaId = zuneMediaId;
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Start, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
}
|
|
else if (previousPlayerState == PlayerState.Paused && _playerState == PlayerState.Playing)
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Resume, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
else if (_playerState == PlayerState.Paused)
|
|
{
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Pause, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
}
|
|
else
|
|
{
|
|
if (_playerState != PlayerState.Stopped)
|
|
return;
|
|
_streamingReportIsOpen = false;
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Stop, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!_streamingReportIsOpen)
|
|
return;
|
|
_streamingReportIsOpen = false;
|
|
Service.Instance.ReportStreamingAction(EStreamingActionType.Stop, _streamingReportMediaInstanceId, new AsyncCompleteHandler(OnStreamingRestrictionResponse));
|
|
}
|
|
}
|
|
|
|
private void SetPlayerState(PlayerState stateNew)
|
|
{
|
|
PlayerState playerState = _playerState;
|
|
if (stateNew != _playerState)
|
|
{
|
|
_playerState = stateNew;
|
|
if (_playerState == PlayerState.Stopped)
|
|
{
|
|
_rewinding.Value = false;
|
|
_fastforwarding.Value = false;
|
|
++_lastKnownSetUriCallID;
|
|
FirePropertyChanged("PlaybackStopped");
|
|
PlaybackStopped?.Invoke(this, null);
|
|
}
|
|
|
|
UpdateSmtcState(stateNew);
|
|
}
|
|
UpdatePropertiesAndCommands();
|
|
ReportStreamingAction(playerState);
|
|
}
|
|
|
|
public void PlayItem(
|
|
object item,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
PlaybackContext playbackContext)
|
|
{
|
|
ArrayListDataSet arrayListDataSet = new()
|
|
{
|
|
item
|
|
};
|
|
PlayItemsWorker(arrayListDataSet, -1, true, playNavigationOptions, playbackContext, null);
|
|
}
|
|
|
|
public void PlayItem(object item) => PlayItem(item, PlayNavigationOptions.NavigateVideosToNowPlaying);
|
|
|
|
public void PlayItem(object item, PlayNavigationOptions playNavigationOptions)
|
|
{
|
|
ArrayListDataSet arrayListDataSet = new()
|
|
{
|
|
item
|
|
};
|
|
PlayItemsWorker(arrayListDataSet, -1, true, playNavigationOptions, null);
|
|
}
|
|
|
|
public void PlayItem(object item, PlaybackContext playbackContext)
|
|
{
|
|
ArrayListDataSet arrayListDataSet = new()
|
|
{
|
|
item
|
|
};
|
|
PlayItemsWorker(arrayListDataSet, -1, true, PlayNavigationOptions.NavigateVideosToNowPlaying, playbackContext, null);
|
|
}
|
|
|
|
public void PlayItems(IList items) => PlayItemsWorker(items, -1, true, PlayNavigationOptions.NavigateVideosToNowPlaying, null);
|
|
|
|
public void PlayItems(IList items, PlayNavigationOptions playNavigationOptions) => PlayItemsWorker(items, -1, true, playNavigationOptions, null);
|
|
|
|
public void PlayItems(
|
|
IList items,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
ContainerPlayMarker containerPlayMarker)
|
|
{
|
|
PlayItemsWorker(items, -1, true, playNavigationOptions, containerPlayMarker);
|
|
}
|
|
|
|
public void PlayItems(
|
|
IList items,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
PlaybackContext playbackContext)
|
|
{
|
|
PlayItemsWorker(items, -1, true, playNavigationOptions, playbackContext, null);
|
|
}
|
|
|
|
public void PlayItems(IList items, PlaybackContext playbackContext) => PlayItemsWorker(items, -1, true, PlayNavigationOptions.NavigateVideosToNowPlaying, playbackContext, null);
|
|
|
|
public void PlayItems(IList items, int startIndex) => PlayItemsWorker(items, startIndex, true, PlayNavigationOptions.NavigateVideosToNowPlaying, null);
|
|
|
|
public void PlayItems(
|
|
IList items,
|
|
int startIndex,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
ContainerPlayMarker containerPlayMarker)
|
|
{
|
|
PlayItemsWorker(items, startIndex, true, PlayNavigationOptions.NavigateVideosToNowPlaying, containerPlayMarker);
|
|
}
|
|
|
|
public void AddToNowPlaying(IList items)
|
|
{
|
|
int count = PlayItemsWorker(items, -1, false, PlayNavigationOptions.NavigateVideosToNowPlaying, null);
|
|
if (count > 0)
|
|
PlaylistManager.Instance.NotifyItemsAdded(-1, count);
|
|
}
|
|
|
|
private int PlayItemsWorker(
|
|
IList items,
|
|
int startIndex,
|
|
bool clearQueue,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
ContainerPlayMarker containerPlayMarker)
|
|
{
|
|
return PlayItemsWorker(items, startIndex, clearQueue, playNavigationOptions, _shellInstance.CurrentPage.PlaybackContext, containerPlayMarker);
|
|
}
|
|
|
|
private int PlayItemsWorker(
|
|
IList items,
|
|
int startIndex,
|
|
bool clearQueue,
|
|
PlayNavigationOptions playNavigationOptions,
|
|
PlaybackContext playbackContext,
|
|
ContainerPlayMarker containerPlayMarker)
|
|
{
|
|
PerfTrace.TraceUICollectionEvent(UICollectionEvent.PlayRequestIssued, "");
|
|
bool hasPlaylist = _playlistCurrent != null;
|
|
int num;
|
|
if (clearQueue || !hasPlaylist)
|
|
{
|
|
if (clearQueue || _playlistPending == null)
|
|
{
|
|
_playlistPending?.Dispose();
|
|
_playlistPending = new NowPlayingList(items, startIndex, playbackContext, playNavigationOptions, _shuffling.Value, containerPlayMarker, _dontPlayMarketplaceTracks);
|
|
num = _playlistPending.Count;
|
|
}
|
|
else
|
|
num = _playlistPending.AddItems(items);
|
|
if (playbackContext == PlaybackContext.QuickMix)
|
|
{
|
|
if (_lastKnownTransportState == PlaybackState.Playing || _lastKnownTransportState == PlaybackState.Paused)
|
|
_player.Stop();
|
|
else
|
|
_playlistPending.PlayWhenReady = true;
|
|
}
|
|
else if (_playlistPending.Count == 0)
|
|
{
|
|
_playlistPending.Dispose();
|
|
_playlistPending = null;
|
|
Stop.Invoke();
|
|
}
|
|
else if (hasPlaylist && (_lastKnownTransportState == PlaybackState.Playing || _lastKnownTransportState == PlaybackState.Paused))
|
|
_player.Stop();
|
|
else
|
|
PlayPendingList();
|
|
}
|
|
else
|
|
{
|
|
num = _playlistCurrent.AddItems(items);
|
|
_playlistPending?.Dispose();
|
|
_playlistPending = null;
|
|
UpdateNextTrack();
|
|
}
|
|
return num;
|
|
}
|
|
|
|
internal void PlayPendingList()
|
|
{
|
|
if (_playlistPending == null)
|
|
return;
|
|
if (_playlistCurrent != null && _playlistCurrent != _playlistPending)
|
|
_playlistCurrent.Dispose();
|
|
_playlistCurrent = _playlistPending;
|
|
_playlistPending = null;
|
|
_playlistCurrent.SetShuffling(_shuffling.Value);
|
|
_playlistCurrent.SetRepeating(_repeating.Value);
|
|
_consecutiveErrors = 0;
|
|
if (_errors.Count > 0)
|
|
{
|
|
FirePropertyChanged("ErrorCount");
|
|
_errors.Clear();
|
|
}
|
|
SetPlayerState(PlayerState.Playing);
|
|
SetUriOnPlayer();
|
|
bool isCurrentVideo = false;
|
|
if (_playlistCurrent != null)
|
|
{
|
|
PlaybackTrack currentTrack = _playlistCurrent.CurrentTrack;
|
|
PlayNavigationOptions navigationOptions = _playlistCurrent.PlayNavigationOptions;
|
|
bool navigate = false;
|
|
if (currentTrack != null && currentTrack.IsVideo)
|
|
isCurrentVideo = true;
|
|
switch (navigationOptions)
|
|
{
|
|
case PlayNavigationOptions.None:
|
|
if (navigate)
|
|
NowPlayingLand.NavigateToLand(navigationOptions == PlayNavigationOptions.NavigateToNowPlayingWithMix, true);
|
|
break;
|
|
|
|
case PlayNavigationOptions.NavigateVideosToNowPlaying:
|
|
if (isCurrentVideo)
|
|
navigate = true;
|
|
goto case PlayNavigationOptions.None;
|
|
|
|
default:
|
|
navigate = true;
|
|
_playlistCurrent.PlayNavigationOptions = PlayNavigationOptions.NavigateVideosToNowPlaying;
|
|
goto case PlayNavigationOptions.None;
|
|
}
|
|
}
|
|
PlayingVideo = isCurrentVideo;
|
|
}
|
|
|
|
public void RemoveFromNowPlaying(IList indices)
|
|
{
|
|
if (_playlistCurrent == null)
|
|
return;
|
|
|
|
bool removed = _playlistCurrent.Remove(indices);
|
|
if (_playlistCurrent.Count == 0)
|
|
Stop.Invoke();
|
|
else if (removed)
|
|
SetUriOnPlayer();
|
|
else
|
|
UpdateNextTrack();
|
|
}
|
|
|
|
public void ReorderNowPlaying(IList indices, int targetIndex)
|
|
{
|
|
if (_playlistCurrent == null)
|
|
return;
|
|
_playlistCurrent.Reorder(indices, targetIndex);
|
|
UpdateNextTrack();
|
|
}
|
|
|
|
public IList GetNextTracks(int count) => _playlistCurrent?.GetNextTracks(count);
|
|
|
|
public IList CreateAlbumListForBackground(IList allAlbums, int totalDesired)
|
|
{
|
|
List<object> objectList1 = new(totalDesired);
|
|
Dictionary<int, object> dictionary = new();
|
|
if (_playlistCurrent != null)
|
|
{
|
|
int num = Math.Min(_playlistCurrent.Count, totalDesired);
|
|
for (int itemIndex = 0; itemIndex < num; ++itemIndex)
|
|
{
|
|
if (_playlistCurrent.TrackList[itemIndex] is LibraryPlaybackTrack track && track.MediaType == MediaType.Track)
|
|
dictionary[track.AlbumLibraryId] = null;
|
|
}
|
|
}
|
|
List<object> objectList2 = new(totalDesired);
|
|
for (int index = 0; index < allAlbums.Count; ++index)
|
|
{
|
|
DataProviderObject allAlbum = (DataProviderObject)allAlbums[index];
|
|
int property = (int)allAlbum.GetProperty("LibraryId");
|
|
if ((bool)allAlbum.GetProperty("HasAlbumArt"))
|
|
{
|
|
if (dictionary.ContainsKey(property))
|
|
objectList1.Add(allAlbum);
|
|
else if (objectList2.Count < totalDesired)
|
|
objectList2.Add(allAlbum);
|
|
}
|
|
if (objectList1.Count >= totalDesired)
|
|
break;
|
|
}
|
|
for (int index = 0; index < objectList2.Count && objectList1.Count < totalDesired; ++index)
|
|
objectList1.Add(objectList2[index]);
|
|
if (objectList1.Count == 0)
|
|
return null;
|
|
foreach (object album in objectList1)
|
|
DisableSlowDataThumbnailExtraction(album);
|
|
int num1 = 0;
|
|
while (objectList1.Count < totalDesired)
|
|
objectList1.Add(objectList1[num1++]);
|
|
Random random = new();
|
|
for (int index1 = objectList1.Count - 1; index1 > 0; --index1)
|
|
{
|
|
int index2 = random.Next(index1 + 1);
|
|
object obj = objectList1[index2];
|
|
objectList1[index2] = objectList1[index1];
|
|
objectList1[index1] = obj;
|
|
}
|
|
return new ListDataSet(objectList1);
|
|
}
|
|
|
|
public void DisableSlowDataThumbnailExtraction(object album)
|
|
{
|
|
if (album is LibraryDataProviderItemBase providerItemBase)
|
|
providerItemBase.SetSlowDataThumbnailExtraction(false);
|
|
}
|
|
|
|
public void Phase2Init()
|
|
{
|
|
if (TryGetPlayerInterop(out var playbackWrapper))
|
|
{
|
|
if (!CanRender3DVideo)
|
|
playbackWrapper.WindowHandle = Application.Window.Handle;
|
|
else
|
|
playbackWrapper.DynamicImage = _videoStream.StreamID;
|
|
}
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncPhase2Init), null);
|
|
}
|
|
|
|
private void AsyncPhase2Init(object arg) => Application.DeferredInvoke(new DeferredInvokeHandler(CompletePhase2Init), arg);
|
|
|
|
private void CompletePhase2Init(object obj)
|
|
{
|
|
AsyncHelper.Run(_player.ChangeVolumeAsync(_volume.Value / 100d));
|
|
|
|
_isSpectrumAvailable = true;
|
|
ConnectAllSpectrumAnimationSources();
|
|
|
|
_taskbarPlayer.Initialize(Application.Window.Handle, new TaskbarPlayerCommandHandler(OnTaskbarPlayerCommand));
|
|
|
|
IsInitialized = true;
|
|
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(DeserializeNowPlayingList), _savedNowPlayingFilename);
|
|
if (RequestedJumpListPin != null)
|
|
FirePropertyChanged("RequestedJumpListPin");
|
|
if (ShuffleAllRequested)
|
|
FirePropertyChanged("ShuffleAllRequested");
|
|
}
|
|
|
|
private void OnTaskbarPlayerCommand(ETaskbarPlayerCommand command, int value)
|
|
{
|
|
switch (command)
|
|
{
|
|
case ETaskbarPlayerCommand.PC_Connect:
|
|
UpdateTaskbarPlayer();
|
|
break;
|
|
case ETaskbarPlayerCommand.PC_Play:
|
|
Play.Invoke();
|
|
break;
|
|
case ETaskbarPlayerCommand.PC_Pause:
|
|
Pause.Invoke();
|
|
break;
|
|
case ETaskbarPlayerCommand.PC_Forward:
|
|
Forward.Invoke();
|
|
break;
|
|
case ETaskbarPlayerCommand.PC_Back:
|
|
Back.Invoke();
|
|
break;
|
|
case ETaskbarPlayerCommand.PC_Rate:
|
|
if (CurrentTrack == null || !CurrentTrack.CanRate)
|
|
break;
|
|
switch ((ETaskbarPlayerState)value)
|
|
{
|
|
case ETaskbarPlayerState.PS_RatingNotRated:
|
|
CurrentTrack.UserRating = 0;
|
|
break;
|
|
case ETaskbarPlayerState.PS_RatingLoveIt:
|
|
CurrentTrack.UserRating = 8;
|
|
break;
|
|
case ETaskbarPlayerState.PS_RatingHateIt:
|
|
CurrentTrack.UserRating = 2;
|
|
break;
|
|
}
|
|
UpdateTaskbarPlayer();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateTaskbarPlayer()
|
|
{
|
|
ETaskbarPlayerState etaskbarPlayerState1 = (ETaskbarPlayerState)(0 | (_playerState == PlayerState.Playing ? 2 : 0) | (_playerState == PlayerState.Paused ? 4 : 0) | (_playerState == PlayerState.Stopped ? 1 : 0));
|
|
ETaskbarPlayerState etaskbarPlayerState2;
|
|
if (CurrentTrack != null && CurrentTrack.CanRate)
|
|
{
|
|
RatingConstants userRating = (RatingConstants)CurrentTrack.UserRating;
|
|
etaskbarPlayerState2 = userRating != RatingConstants.Unrated ? (userRating > RatingConstants.MaxHateIt ? etaskbarPlayerState1 | ETaskbarPlayerState.PS_RatingLoveIt : etaskbarPlayerState1 | ETaskbarPlayerState.PS_RatingHateIt) : etaskbarPlayerState1 | ETaskbarPlayerState.PS_RatingNotRated;
|
|
}
|
|
else
|
|
etaskbarPlayerState2 = etaskbarPlayerState1 | ETaskbarPlayerState.PS_RatingNotRated;
|
|
ETaskbarPlayerState state = etaskbarPlayerState2 | (Play.Available ? ETaskbarPlayerState.PS_CanPlay : 0) | (Pause.Available ? ETaskbarPlayerState.PS_CanPause : 0) | (Forward.Available ? ETaskbarPlayerState.PS_CanForward : 0) | (Back.Available ? ETaskbarPlayerState.PS_CanBack : 0);
|
|
if (CurrentTrack != null && CurrentTrack.CanRate)
|
|
state |= ETaskbarPlayerState.PS_CanRate;
|
|
_taskbarPlayer.UpdateToolbar(state);
|
|
}
|
|
|
|
public int CreateSpectrumAnimationSource(
|
|
int numBands,
|
|
bool outputFrequencyData,
|
|
bool outputWaveformData,
|
|
bool enableStereoOutput)
|
|
{
|
|
Dictionary<string, int> dictionary = new();
|
|
for (int index = 0; index < numBands; ++index)
|
|
{
|
|
if (outputFrequencyData)
|
|
{
|
|
if (!enableStereoOutput)
|
|
{
|
|
dictionary[$"Frequency{index}"] = index;
|
|
}
|
|
else
|
|
{
|
|
dictionary[$"FrequencyL{index}"] = index;
|
|
dictionary[$"FrequencyR{index}"] = index + 1024;
|
|
}
|
|
}
|
|
if (outputWaveformData)
|
|
{
|
|
if (!enableStereoOutput)
|
|
{
|
|
dictionary[$"Waveform{index}"] = index + 2048;
|
|
}
|
|
else
|
|
{
|
|
dictionary[$"WaveformL{index}"] = index + 2048;
|
|
dictionary[$"WaveformR{index}"] = index + 3072;
|
|
}
|
|
}
|
|
}
|
|
int externalAnimationInput = Application.CreateExternalAnimationInput(dictionary);
|
|
SpectrumOutputConfig spectrumOutputConfig = new()
|
|
{
|
|
SourceId = (uint)externalAnimationInput,
|
|
NumBands = (uint)numBands,
|
|
Frequency = outputFrequencyData,
|
|
Waveform = outputWaveformData,
|
|
Stereo = enableStereoOutput,
|
|
IsConnected = _isSpectrumAvailable
|
|
};
|
|
_spectrumConfigList.Add(spectrumOutputConfig);
|
|
|
|
if (_isSpectrumAvailable && TryGetPlayerInterop(out var playbackWrapper))
|
|
playbackWrapper.ConnectAnimationsToSpectrumAnalyzer(spectrumOutputConfig.SourceId, spectrumOutputConfig.NumBands, spectrumOutputConfig.Frequency, spectrumOutputConfig.Waveform, spectrumOutputConfig.Stereo);
|
|
|
|
return externalAnimationInput;
|
|
}
|
|
|
|
public void DisposeSpectrumAnimationSource(int inputSourceId)
|
|
{
|
|
if (inputSourceId <= 0)
|
|
return;
|
|
for (int index = 0; index < _spectrumConfigList.Count; ++index)
|
|
{
|
|
SpectrumOutputConfig spectrumConfig = _spectrumConfigList[index];
|
|
if (spectrumConfig.SourceId == inputSourceId)
|
|
{
|
|
if (spectrumConfig.IsConnected && TryGetPlayerInterop(out var playbackWrapper))
|
|
{
|
|
playbackWrapper.DisconnectAnimationsFromSpectrumAnalyzer(spectrumConfig.SourceId);
|
|
spectrumConfig.IsConnected = false;
|
|
}
|
|
|
|
Application.DisposeExternalAnimationInput(inputSourceId);
|
|
_spectrumConfigList.RemoveAt(index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ConnectAllSpectrumAnimationSources()
|
|
{
|
|
for (int index = 0; index < _spectrumConfigList.Count; ++index)
|
|
{
|
|
SpectrumOutputConfig spectrumConfig = _spectrumConfigList[index];
|
|
if (!spectrumConfig.IsConnected && TryGetPlayerInterop(out var playbackWrapper))
|
|
{
|
|
playbackWrapper.ConnectAnimationsToSpectrumAnalyzer(spectrumConfig.SourceId, spectrumConfig.NumBands, spectrumConfig.Frequency, spectrumConfig.Waveform, spectrumConfig.Stereo);
|
|
spectrumConfig.IsConnected = true;
|
|
_spectrumConfigList[index] = spectrumConfig;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DisconnectAllSpectrumAnimationSources()
|
|
{
|
|
if (!_isSpectrumAvailable)
|
|
return;
|
|
for (int index = 0; index < _spectrumConfigList.Count; ++index)
|
|
{
|
|
SpectrumOutputConfig spectrumConfig = _spectrumConfigList[index];
|
|
if (spectrumConfig.IsConnected && TryGetPlayerInterop(out var playbackWrapper))
|
|
{
|
|
playbackWrapper.DisconnectAnimationsFromSpectrumAnalyzer(spectrumConfig.SourceId);
|
|
spectrumConfig.IsConnected = false;
|
|
_spectrumConfigList[index] = spectrumConfig;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ResumeLastNowPlayingHandler()
|
|
{
|
|
if (!IsInitialized)
|
|
{
|
|
_resumeLastNowPlayingRequested = true;
|
|
}
|
|
else if(!_isPlaying)
|
|
{
|
|
Play.Invoke();
|
|
}
|
|
}
|
|
|
|
private void OnShellPropertyChanged(object sender, PropertyChangedEventArgs args)
|
|
{
|
|
if (args.PropertyName != "CurrentPage")
|
|
return;
|
|
_pagePlaybackContext = _shellInstance.CurrentPage.PlaybackContext;
|
|
UpdatePropertiesAndCommands();
|
|
}
|
|
|
|
private void OnDownloadProgressed(Guid zuneMediaId, float percent)
|
|
{
|
|
if (CurrentTrack == null || !(CurrentTrack.ZuneMediaId == zuneMediaId))
|
|
return;
|
|
CurrentTrackDownloadProgress = percent;
|
|
}
|
|
|
|
private void OnShufflingChanged(object sender, EventArgs e)
|
|
{
|
|
if (_playlistCurrent != null)
|
|
{
|
|
_playlistCurrent.SetShuffling(_shuffling.Value);
|
|
UpdateNextTrack();
|
|
}
|
|
UpdateShufflingDescription();
|
|
PersistSettings();
|
|
SQMLog.Log(SQMDataId.ShuffleClicks, 1);
|
|
}
|
|
|
|
private void UpdateShufflingDescription() => _shuffling.Description = Shell.LoadString(!_shuffling.Value ? StringId.IDS_SHUFFLE_ON : StringId.IDS_SHUFFLE_OFF);
|
|
|
|
private void OnRepeatingChanged(object sender, EventArgs e)
|
|
{
|
|
if (_playlistCurrent != null)
|
|
{
|
|
_playlistCurrent.SetRepeating(_repeating.Value);
|
|
UpdateNextTrack();
|
|
}
|
|
SQMLog.Log(SQMDataId.RepeatClicks, 1);
|
|
UpdateRepeatingDescription();
|
|
PersistSettings();
|
|
}
|
|
|
|
private void UpdateRepeatingDescription() => _repeating.Description = Shell.LoadString(!_repeating.Value ? StringId.IDS_REPEAT_ON : StringId.IDS_REPEAT_OFF);
|
|
|
|
private void OnMutingChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateMutingDescription();
|
|
|
|
if (_muted.Value)
|
|
_player.Mute();
|
|
else
|
|
AsyncHelper.Run(_player.ChangeVolumeAsync(0.5));
|
|
|
|
SQMLog.Log(SQMDataId.VolumeMuteClicks, 1);
|
|
PersistSettings();
|
|
}
|
|
|
|
private void UpdateMutingDescription() => _muted.Description = Shell.LoadString(!_muted.Value ? StringId.IDS_MUTE : StringId.IDS_UNMUTE);
|
|
|
|
private void OnShowTotalTimeChanged(object sender, EventArgs e) => PersistSettings();
|
|
|
|
private void OnShowNowPlayingListChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateShowNowPlayingListDescription();
|
|
PersistSettings();
|
|
}
|
|
|
|
private void UpdateShowNowPlayingListDescription() => _showNowPlayingList.Description = Shell.LoadString(!_showNowPlayingList.Value ? StringId.IDS_NOWPLAYINGLIST_ON : StringId.IDS_NOWPLAYINGLIST_OFF);
|
|
|
|
private void OnPlayingChanged(object sender, EventArgs e)
|
|
{
|
|
}
|
|
|
|
private void OnFastforwardingChanged(object sender, EventArgs e)
|
|
{
|
|
if (_fastforwarding.Value && (_currentTrack == null || !_currentTrack.IsVideo
|
|
|| (TryGetPlayerInterop(out var playbackWrapper) && playbackWrapper.CanChangeVideoRate)))
|
|
{
|
|
_rewinding.Value = false;
|
|
AsyncHelper.Run(_player.ChangePlaybackSpeedAsync(5.0));
|
|
}
|
|
else
|
|
AsyncHelper.Run(_player.ChangePlaybackSpeedAsync(1.0));
|
|
}
|
|
|
|
private void OnRewindingChanged(object sender, EventArgs e)
|
|
{
|
|
if (_fastforwarding.Value && (_currentTrack == null || !_currentTrack.IsVideo
|
|
|| (TryGetPlayerInterop(out var playbackWrapper) && playbackWrapper.CanChangeVideoRate)))
|
|
{
|
|
_rewinding.Value = false;
|
|
AsyncHelper.Run(_player.ChangePlaybackSpeedAsync(-5.0));
|
|
}
|
|
else
|
|
AsyncHelper.Run(_player.ChangePlaybackSpeedAsync(1.0));
|
|
}
|
|
|
|
private void OnFastforwardHotkeyPressed(object sender, EventArgs e) => _fastforwarding.Value = !_fastforwarding.Value;
|
|
|
|
private void OnRewindHotkeyPressed(object sender, EventArgs e) => _rewinding.Value = !_rewinding.Value;
|
|
|
|
private void OnPlaybackStatusChanged(object sender, PlaybackState e) => Application.DeferredInvoke(new DeferredInvokeHandler(DeferredPlaybackStatusChanged), new object[2]
|
|
{
|
|
_player.PlaybackState,
|
|
false // EndOfMedia
|
|
});
|
|
|
|
private void OnTransportStatusChanged(object sender, EventArgs e) => Application.DeferredInvoke(new DeferredInvokeHandler(DeferredTransportStatusChanged), new object[3]
|
|
{
|
|
_player.PlaybackState,
|
|
false, // _playbackWrapper.EndOfMedia,
|
|
true // _playbackWrapper.CanSeek
|
|
});
|
|
|
|
private void OnTransportPositionChanged(object sender, TimeSpan e) => Application.DeferredInvoke(new DeferredInvokeHandler(DeferredTransportPositionChanged), e);
|
|
|
|
private void OnUriSet(object sender, EventArgs e)
|
|
{
|
|
if (_player.CurrentSource?.MediaConfig is ZuneMediaSourceConfig zuneSrcCfg)
|
|
{
|
|
var args = new object[2]
|
|
{
|
|
zuneSrcCfg.MediaSourceUri,
|
|
0, // TODO: playbackWrapper.CurrentUriID
|
|
};
|
|
Application.DeferredInvoke(new DeferredInvokeHandler(DeferredUriSet), args);
|
|
}
|
|
}
|
|
|
|
private void OnAlertSent(Announcement alert) => Application.DeferredInvoke(new DeferredInvokeHandler(DeferredAlertHandler), alert);
|
|
|
|
private void OnPlayerPropertyChanged(object sender, PlayerPropertyChangedEventArgs e)
|
|
{
|
|
if (e.Key == "presentationinfo")
|
|
Application.DeferredInvoke(new DeferredInvokeHandler(DeferredPresentationInfoChangedHandler), e.Value);
|
|
else if (e.Key == "volumeinfo")
|
|
Application.DeferredInvoke(new DeferredInvokeHandler(DeferredVolumeInfoChangedHandler), e.Value);
|
|
else if (e.Key == "canchangevideorate")
|
|
Application.DeferredInvoke(new DeferredInvokeHandler(DeferredCanChangeVideoRateHandler), e.Value);
|
|
}
|
|
|
|
private void OnVolumeControlChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName != "Value")
|
|
return;
|
|
|
|
_muted.Value = false;
|
|
_player.ChangeVolumeAsync(_volume.Value / 100f);
|
|
SQMLog.Log(SQMDataId.VolumeAdjustmentClicks, 1);
|
|
PersistSettings();
|
|
}
|
|
|
|
private void OnVideoDetailsChanged(object sender, EventArgs args)
|
|
{
|
|
if (TryGetPlayerInterop(out var playbackWrapper))
|
|
{
|
|
playbackWrapper.VideoPosition = new VideoWindow(_videoStream.DisplayPosition.X, _videoStream.DisplayPosition.Y, _videoStream.DisplayPosition.X + _videoStream.DisplaySize.Width, _videoStream.DisplayPosition.Y + _videoStream.DisplaySize.Height);
|
|
playbackWrapper.ShowGDIVideo = _videoStream.DisplayVisibility;
|
|
}
|
|
}
|
|
|
|
private void DeferredPlaybackStatusChanged(object obj)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
object[] objArray = (object[])obj;
|
|
var playerState = (PlaybackState)objArray[0];
|
|
bool endOfMedia = (bool)objArray[1];
|
|
bool startedOpening = false;
|
|
switch (playerState)
|
|
{
|
|
case PlaybackState.None:
|
|
if (Playing)
|
|
{
|
|
SetUriOnPlayer();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PlaybackState.Loading:
|
|
_rewinding.Value = false;
|
|
_fastforwarding.Value = false;
|
|
startedOpening = true;
|
|
break;
|
|
|
|
case PlaybackState.Loaded:
|
|
if (Playing && _lastKnownPlaybackTrack != null)
|
|
{
|
|
_lastKnownPlaybackTrack.OnEndPlayback(endOfMedia);
|
|
_lastKnownPlaybackTrack = _lastKnownPreparedTrack;
|
|
_lastKnownPreparedTrack = null;
|
|
_lastKnownPlaybackTrack?.OnBeginPlayback(null);
|
|
}
|
|
|
|
// TODO: Get duration of track
|
|
CurrentTrackDuration = 0f; // _playbackWrapper.Duration >= 0L ? _playbackWrapper.Duration / 1E+07f : 0.0f;
|
|
break;
|
|
}
|
|
|
|
_lastKnownPlayerState = playerState;
|
|
if (_opening == startedOpening)
|
|
return;
|
|
Opening = startedOpening;
|
|
}
|
|
|
|
private void ShowNotification()
|
|
{
|
|
if (_nowPlayingNotification == null)
|
|
{
|
|
_nowPlayingNotification = new NowPlayingNotification();
|
|
NotificationArea.Instance.Add(_nowPlayingNotification);
|
|
}
|
|
else
|
|
NotificationArea.Instance.ForceToFront(_nowPlayingNotification);
|
|
}
|
|
|
|
private void HideNotification()
|
|
{
|
|
if (_nowPlayingNotification == null)
|
|
return;
|
|
NotificationArea.Instance.Remove(_nowPlayingNotification);
|
|
_nowPlayingNotification = null;
|
|
}
|
|
|
|
public void ShowPreparingNotification()
|
|
{
|
|
HideNotification();
|
|
HidePreparingNotification(false);
|
|
NotificationArea.Instance.Add(new PreparingPlayNotification());
|
|
}
|
|
|
|
public void HidePreparingNotification() => HidePreparingNotification(true);
|
|
|
|
private void HidePreparingNotification(bool restoreNowPlayingNotification)
|
|
{
|
|
NotificationArea.Instance.RemoveAll(NotificationTask.PreparingPlay, NotificationState.Normal);
|
|
if (!restoreNowPlayingNotification || _playlistCurrent == null)
|
|
return;
|
|
ShowNotification();
|
|
}
|
|
|
|
private void DeferredTransportStatusChanged(object obj)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
object[] objArray = (object[])obj;
|
|
var playerState = (PlaybackState)objArray[0];
|
|
bool endOfMedia = (bool)objArray[1];
|
|
IsSeekEnabled = (bool)objArray[2];
|
|
if (playerState == PlaybackState.Loading)
|
|
{
|
|
Buffering = true;
|
|
UpdatePropertiesAndCommands();
|
|
}
|
|
else
|
|
{
|
|
if (Buffering)
|
|
{
|
|
Buffering = false;
|
|
UpdatePropertiesAndCommands();
|
|
}
|
|
switch (playerState)
|
|
{
|
|
case PlaybackState.Paused:
|
|
bool endOfQueue = _playlistCurrent != null && _playlistCurrent.Count == 1 && _repeating.Value && endOfMedia;
|
|
if (_lastKnownPlaybackTrack != null)
|
|
{
|
|
_lastKnownPlaybackTrack.OnEndPlayback(endOfMedia);
|
|
if (!endOfQueue)
|
|
_lastKnownPlaybackTrack = null;
|
|
}
|
|
if (_playlistPending != null)
|
|
{
|
|
if (_playlistPending.TrackList != null && _playlistPending.TrackList.Count > 0)
|
|
{
|
|
PlayPendingList();
|
|
break;
|
|
}
|
|
SetPlayerState(PlayerState.Stopped);
|
|
_playlistPending.PlayWhenReady = true;
|
|
break;
|
|
}
|
|
if (_lastKnownPreparedTrack != null)
|
|
{
|
|
_lastKnownPlaybackTrack = _lastKnownPreparedTrack;
|
|
_lastKnownPlaybackTrack.OnBeginPlayback(null);
|
|
if (Playing)
|
|
_player.Resume();
|
|
_lastKnownPreparedTrack = null;
|
|
break;
|
|
}
|
|
if (endOfQueue && _lastKnownPlaybackTrack != null)
|
|
{
|
|
_lastKnownPlaybackTrack.OnBeginPlayback(null);
|
|
AsyncHelper.Run(_player.SeekAsync(TimeSpan.Zero));
|
|
_player.Resume();
|
|
break;
|
|
}
|
|
_playlistCurrent?.ResetForReplay();
|
|
SetPlayerState(PlayerState.Stopped);
|
|
// TODO: _playbackWrapper.Close();
|
|
break;
|
|
|
|
case PlaybackState.Playing:
|
|
PerfTrace.TraceUICollectionEvent(UICollectionEvent.PlayRequestComplete, "");
|
|
UseSmtc();
|
|
break;
|
|
}
|
|
}
|
|
_lastKnownTransportState = playerState;
|
|
}
|
|
|
|
bool isInit = false;
|
|
private void UseSmtc()
|
|
{
|
|
// Use SMTC when available
|
|
#if WINDOWS
|
|
if (Microsoft.WinRT.ApiInformation.IsTypePresent("Windows.Media.SystemMediaTransportControls")
|
|
&& !OSVersion.IsLessThanWin10())
|
|
{
|
|
// On Windows 8.1 / 10 / 11
|
|
var _systemMediaTransportControls = Windows.Media.SystemMediaTransportControlsInterop.GetForWindow(Application.Window.Handle);
|
|
_systemMediaTransportControls.IsPlayEnabled = true;
|
|
_systemMediaTransportControls.IsPauseEnabled = true;
|
|
_systemMediaTransportControls.IsStopEnabled = true;
|
|
_systemMediaTransportControls.IsNextEnabled = true;
|
|
_systemMediaTransportControls.IsPreviousEnabled = true;
|
|
|
|
if (!isInit)
|
|
{
|
|
isInit = true;
|
|
_systemMediaTransportControls.ButtonPressed += (e, args) =>
|
|
{
|
|
Action<object, EventArgs> method = args.Button switch
|
|
{
|
|
Windows.Media.SystemMediaTransportControlsButton.Play => OnPlayClicked,
|
|
Windows.Media.SystemMediaTransportControlsButton.Pause => OnPauseClicked,
|
|
Windows.Media.SystemMediaTransportControlsButton.Stop => OnStopClicked,
|
|
Windows.Media.SystemMediaTransportControlsButton.Next => OnForwardClicked,
|
|
Windows.Media.SystemMediaTransportControlsButton.Previous => OnBackClicked,
|
|
|
|
_ => throw new NotImplementedException()
|
|
};
|
|
|
|
Application.DeferredInvoke(_ => method(null, null), null);
|
|
};
|
|
}
|
|
|
|
// Get the updater
|
|
Windows.Media.SystemMediaTransportControlsDisplayUpdater updater = _systemMediaTransportControls.DisplayUpdater;
|
|
|
|
// Music metadata
|
|
if (_lastKnownPlaybackTrack != null)
|
|
{
|
|
updater.Type = _lastKnownPlaybackTrack.MediaType == MediaType.Video
|
|
? Windows.Media.MediaPlaybackType.Video
|
|
: Windows.Media.MediaPlaybackType.Music;
|
|
updater.MusicProperties.Title = _lastKnownPlaybackTrack.Title;
|
|
|
|
if (_lastKnownPlaybackTrack is LibraryPlaybackTrack track)
|
|
{
|
|
updater.MusicProperties.Artist = track.DisplayArtist;
|
|
}
|
|
}
|
|
|
|
updater.Update();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void UpdateSmtcState(PlayerState stateNew)
|
|
{
|
|
#if WINDOWS
|
|
if (Microsoft.WinRT.ApiInformation.IsTypePresent("Windows.Media.SystemMediaTransportControls")
|
|
&& !OSVersion.IsLessThanWin10())
|
|
{
|
|
// Windows 8.1 doesn't seem to support non-Metro apps using this API.
|
|
|
|
// On Windows 8.1 / 10 / 11
|
|
var _systemMediaTransportControls = Windows.Media.SystemMediaTransportControlsInterop.GetForWindow(Application.Window.Handle);
|
|
_systemMediaTransportControls.PlaybackStatus = stateNew switch
|
|
{
|
|
PlayerState.Stopped => Windows.Media.MediaPlaybackStatus.Stopped,
|
|
PlayerState.Playing => Windows.Media.MediaPlaybackStatus.Playing,
|
|
PlayerState.Paused => Windows.Media.MediaPlaybackStatus.Paused,
|
|
_ => throw new NotImplementedException(),
|
|
};
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private void DeferredTransportPositionChanged(object obj)
|
|
{
|
|
if (IsDisposed || obj is not TimeSpan position)
|
|
return;
|
|
|
|
_lastKnownPosition = position.Ticks;
|
|
CurrentTrackPosition = _lastKnownPosition / 1E+07f;
|
|
|
|
if (_lastKnownPlaybackTrack == null)
|
|
return;
|
|
|
|
if (_lastKnownPosition > 0L)
|
|
_consecutiveErrors = 0;
|
|
|
|
_lastKnownPlaybackTrack.OnPositionChanged(_lastKnownPosition);
|
|
}
|
|
|
|
private void DeferredUriSet(object obj)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
object[] objArray = (object[])obj;
|
|
string str = (string)objArray[0];
|
|
int num1 = (int)objArray[1];
|
|
_lastKnownPreparedTrack = null;
|
|
int num2;
|
|
for (num2 = 0; num2 < _tracksSubmittedToPlayer.Count; ++num2)
|
|
{
|
|
PlaybackTrack playbackTrack = _tracksSubmittedToPlayer[num2];
|
|
if (playbackTrack.PlaybackID == num1)
|
|
{
|
|
_lastKnownPreparedTrack = playbackTrack;
|
|
++num2;
|
|
break;
|
|
}
|
|
}
|
|
_tracksSubmittedToPlayer.RemoveRange(0, num2);
|
|
if (_lastKnownPreparedTrack == null || _tracksSubmittedToPlayer.Count != 0)
|
|
return;
|
|
_playlistCurrent?.SyncCurrentTrackTo(_lastKnownPreparedTrack);
|
|
UpdateNextTrack();
|
|
}
|
|
|
|
private void DeferredAlertHandler(object obj)
|
|
{
|
|
if (IsDisposed || obj is not Announcement announcement)
|
|
return;
|
|
PlaybackTrack playbackTrack = null;
|
|
if (CurrentTrack != null && CurrentTrack.PlaybackID == announcement.PlaybackID)
|
|
{
|
|
playbackTrack = CurrentTrack;
|
|
}
|
|
else
|
|
{
|
|
for (int index = _tracksSubmittedToPlayer.Count - 1; index >= 0; --index)
|
|
{
|
|
if (_tracksSubmittedToPlayer[index].PlaybackID == announcement.PlaybackID)
|
|
{
|
|
playbackTrack = _tracksSubmittedToPlayer[index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (playbackTrack != null)
|
|
{
|
|
++_consecutiveErrors;
|
|
_errors[playbackTrack] = announcement.HResult;
|
|
_playlistCurrent?.SyncCurrentTrackTo(playbackTrack);
|
|
FirePropertyChanged("ErrorCount");
|
|
}
|
|
bool flag = announcement.HResult == HRESULT._NS_E_CD_BUSY;
|
|
if (_consecutiveErrors < 5 && !flag && Playing && (_playlistCurrent != null && _playlistCurrent.CanAdvance))
|
|
{
|
|
_playlistCurrent.Advance();
|
|
// TODO: _playbackWrapper.Close();
|
|
}
|
|
else
|
|
{
|
|
SetPlayerState(PlayerState.Stopped);
|
|
//_playbackWrapper.Close();
|
|
if (_lastKnownPlaybackTrack != null)
|
|
{
|
|
_lastKnownPlaybackTrack.OnEndPlayback(false);
|
|
_lastKnownPlaybackTrack = null;
|
|
}
|
|
if (flag || !ShowErrors)
|
|
return;
|
|
ErrorDialogInfo.Show(announcement.HResult, Shell.LoadString(StringId.IDS_PLAYBACK_ERROR));
|
|
}
|
|
}
|
|
|
|
private void DeferredPresentationInfoChangedHandler(object obj)
|
|
{
|
|
if (IsDisposed || _videoStream == null)
|
|
return;
|
|
PresentationInfo presentationInfo = (PresentationInfo)obj;
|
|
_videoStream.ContentWidth = presentationInfo.ContentWidth;
|
|
_videoStream.ContentHeight = presentationInfo.ContentHeight;
|
|
_videoStream.ContentAspectWidth = presentationInfo.ContentAspectWidth;
|
|
_videoStream.ContentAspectHeight = presentationInfo.ContentAspectHeight;
|
|
_videoStream.ContentOverscanPercent = presentationInfo.NeedOverscan ? 0.1f : 0.0f;
|
|
FirePropertyChanged("VideoStream");
|
|
}
|
|
|
|
private void DeferredVolumeInfoChangedHandler(object obj)
|
|
{
|
|
_volume.Value = (float)(_player.Volume * 100);
|
|
FirePropertyChanged("Volume");
|
|
_muted.Value = _player.Volume == 0;
|
|
FirePropertyChanged("Mute");
|
|
}
|
|
|
|
private void DeferredCanChangeVideoRateHandler(object obj)
|
|
{
|
|
if (_currentTrack == null || !_currentTrack.IsVideo)
|
|
return;
|
|
|
|
_forward.Available = !TryGetPlayerInterop(out var playbackWrapper) && playbackWrapper.CanChangeVideoRate;
|
|
}
|
|
|
|
private void SetUriOnPlayer()
|
|
{
|
|
PlaybackTrack track = null;
|
|
if (_playlistCurrent != null)
|
|
track = _playlistCurrent.CurrentTrack;
|
|
PlaybackTrack nextTrack = null;
|
|
if (_playlistCurrent != null && _playlistCurrent.Count > 1)
|
|
nextTrack = _playlistCurrent.NextTrack;
|
|
if (track != null)
|
|
{
|
|
SetUrisOnPlayerAsync(track, nextTrack);
|
|
}
|
|
else
|
|
{
|
|
SetPlayerState(PlayerState.Stopped);
|
|
//_playbackWrapper.Close();
|
|
UpdatePropertiesAndCommands();
|
|
}
|
|
}
|
|
|
|
private void SetNextUriOnPlayer()
|
|
{
|
|
PlaybackTrack nextTrack = null;
|
|
if (_playlistCurrent != null)
|
|
nextTrack = _playlistCurrent.NextTrack;
|
|
if (nextTrack != null && _playlistCurrent.Count > 1)
|
|
SetUrisOnPlayerAsync(null, nextTrack);
|
|
//else
|
|
// _playbackWrapper.CancelNext();
|
|
}
|
|
|
|
private void SetUrisOnPlayerAsync(PlaybackTrack track, PlaybackTrack nextTrack)
|
|
{
|
|
int myID = ++_lastKnownSetUriCallID;
|
|
ThreadPool.QueueUserWorkItem(args =>
|
|
{
|
|
if (track != null)
|
|
{
|
|
HRESULT hr = track.GetURI(out string trackUri);
|
|
if (string.IsNullOrEmpty(trackUri))
|
|
trackUri = ".:* INVALID URI *:.";
|
|
|
|
if (hr.IsSuccess)
|
|
Application.DeferredInvoke(delegate
|
|
{
|
|
if (IsDisposed || myID != _lastKnownSetUriCallID)
|
|
return;
|
|
|
|
PlaybackItem item = ZuneMediaSourceConfig.CreatePlaybackItem(trackUri, track.PlaybackID);
|
|
_player.PlaySync(item);
|
|
|
|
ReportStreamingAction(PlayerState.Stopped);
|
|
_tracksSubmittedToPlayer.Remove(track);
|
|
_tracksSubmittedToPlayer.Add(track);
|
|
if (nextTrack != null)
|
|
return;
|
|
|
|
// TODO: _playbackWrapper.CancelNext();
|
|
UpdatePropertiesAndCommands();
|
|
}, null);
|
|
else
|
|
OnAlertSent(new Announcement()
|
|
{
|
|
HResult = hr.Int,
|
|
PlaybackID = track.PlaybackID
|
|
});
|
|
}
|
|
|
|
if (nextTrack == null)
|
|
return;
|
|
|
|
HRESULT nextTrackHr = nextTrack.GetURI(out string nextTrackUri);
|
|
if (string.IsNullOrEmpty(nextTrackUri))
|
|
nextTrackUri = ".:* INVALID URI *:.";
|
|
if (!nextTrackHr.IsSuccess)
|
|
return;
|
|
|
|
Application.DeferredInvoke(delegate
|
|
{
|
|
if (IsDisposed || myID != _lastKnownSetUriCallID)
|
|
return;
|
|
|
|
PlaybackItem item = ZuneMediaSourceConfig.CreatePlaybackItem(nextTrackUri, track.PlaybackID);
|
|
_player.PreloadSync(item);
|
|
|
|
_tracksSubmittedToPlayer.Remove(nextTrack);
|
|
_tracksSubmittedToPlayer.Add(nextTrack);
|
|
UpdatePropertiesAndCommands();
|
|
}, null);
|
|
}, null);
|
|
}
|
|
|
|
private void UpdateNextTrack()
|
|
{
|
|
SetNextUriOnPlayer();
|
|
UpdatePropertiesAndCommands();
|
|
}
|
|
|
|
private bool AreContextsCompatible(PlaybackContext contextCurrent, PlaybackContext contextNext) => contextCurrent == PlaybackContext.None || contextNext == contextCurrent;
|
|
|
|
public bool IsCurrentPlaylistContextCompatible(PlaybackContext context)
|
|
{
|
|
return _playlistCurrent != null && AreContextsCompatible(context, _playlistCurrent.PlaybackContext);
|
|
}
|
|
|
|
private void UpdatePropertiesAndCommands()
|
|
{
|
|
bool isPlaying = _playerState == PlayerState.Playing;
|
|
ArrayListDataSet arrayListDataSet;
|
|
bool isPlaylist;
|
|
bool isContextCompatible;
|
|
PlaybackTrack playbackTrack;
|
|
int currentTrackIndex;
|
|
bool supportsShuffle;
|
|
if (_playlistCurrent != null)
|
|
{
|
|
arrayListDataSet = _playlistCurrent.TrackList;
|
|
isPlaylist = true;
|
|
isContextCompatible = IsCurrentPlaylistContextCompatible(_pagePlaybackContext);
|
|
playbackTrack = _playlistCurrent.CurrentTrack;
|
|
currentTrackIndex = _playlistCurrent.ListIndexOfCurrentTrack;
|
|
supportsShuffle = _playlistCurrent.QuickMixSession == null;
|
|
}
|
|
else
|
|
{
|
|
isPlaylist = false;
|
|
arrayListDataSet = null;
|
|
isContextCompatible = true;
|
|
playbackTrack = null;
|
|
currentTrackIndex = -1;
|
|
PlayingVideo = false;
|
|
supportsShuffle = true;
|
|
}
|
|
|
|
if (arrayListDataSet != _currentPlaylist)
|
|
{
|
|
_currentPlaylist = arrayListDataSet;
|
|
FirePropertyChanged("CurrentPlaylist");
|
|
}
|
|
if (isPlaylist != _hasPlaylist)
|
|
{
|
|
_hasPlaylist = isPlaylist;
|
|
FirePropertyChanged("HasPlaylist");
|
|
}
|
|
if (supportsShuffle != _playlistSupportsShuffle)
|
|
{
|
|
_playlistSupportsShuffle = supportsShuffle;
|
|
FirePropertyChanged("PlaylistSupportsShuffle");
|
|
}
|
|
if (isPlaying != _isPlaying)
|
|
{
|
|
_isPlaying = isPlaying;
|
|
FirePropertyChanged("Playing");
|
|
if (_isPlaying)
|
|
{
|
|
HasPlayed = true;
|
|
_currentPlayStartTime = DateTime.Now;
|
|
}
|
|
else if (_currentPlayStartTime != DateTime.MinValue)
|
|
{
|
|
Telemetry.Instance.ReportPlaybackTime((int)DateTime.Now.Subtract(_currentPlayStartTime).TotalSeconds);
|
|
_currentPlayStartTime = DateTime.MinValue;
|
|
}
|
|
}
|
|
if (currentTrackIndex != _currentTrackIndex)
|
|
{
|
|
if (_currentPlayStartTime != DateTime.MinValue)
|
|
{
|
|
Telemetry.Instance.ReportPlaybackTime((int)DateTime.Now.Subtract(_currentPlayStartTime).TotalSeconds);
|
|
_currentPlayStartTime = DateTime.Now;
|
|
}
|
|
_currentTrackIndex = currentTrackIndex;
|
|
FirePropertyChanged("CurrentTrackIndex");
|
|
}
|
|
if (!ReferenceEquals(playbackTrack, _currentTrack))
|
|
{
|
|
if (_currentTrack != null)
|
|
_currentTrack.RatingChanged.Invoked -= _currentTrackRatingChangedEventHandler;
|
|
_currentTrack = playbackTrack;
|
|
if (_currentTrack != null)
|
|
_currentTrack.RatingChanged.Invoked += _currentTrackRatingChangedEventHandler;
|
|
FirePropertyChanged("CurrentTrack");
|
|
OnCurrentTrackRatingChanged(this, null);
|
|
CurrentTrackDownloadProgress = 0.0f;
|
|
if (_currentTrack != null)
|
|
ShowNotification();
|
|
else
|
|
HideNotification();
|
|
ZoomScaleFactor = 0;
|
|
}
|
|
if (isContextCompatible != _isContextCompatible)
|
|
{
|
|
_isContextCompatible = isContextCompatible;
|
|
FirePropertyChanged("IsPlaybackContextCompatible");
|
|
}
|
|
UpdateAvailabilityOfCommands();
|
|
if (Playing && CurrentTrack != null && (CurrentTrack.IsVideo && CurrentTrack.IsStreaming))
|
|
{
|
|
_isStreamingTimeoutTimer.Enabled = false;
|
|
IsStreamingVideo = true;
|
|
SupressDownloads = true;
|
|
}
|
|
else
|
|
{
|
|
if (SupressDownloads)
|
|
_isStreamingTimeoutTimer.Enabled = true;
|
|
IsStreamingVideo = false;
|
|
}
|
|
}
|
|
|
|
private void UpdateAvailabilityOfCommands()
|
|
{
|
|
bool playing = Playing;
|
|
bool hasPlaylist = _playlistCurrent != null;
|
|
_play.Available = !playing && hasPlaylist && !Buffering;
|
|
_pause.Available = playing && !Buffering;
|
|
_stop.Available = hasPlaylist;
|
|
if (_playerState == PlayerState.Stopped || Buffering)
|
|
{
|
|
_forward.Available = false;
|
|
_back.Available = false;
|
|
}
|
|
else if (_playerState == PlayerState.Playing)
|
|
{
|
|
if (_currentTrack == null)
|
|
{
|
|
_forward.Available = false;
|
|
_back.Available = false;
|
|
}
|
|
else
|
|
{
|
|
_forward.Available = !_currentTrack.IsVideo || (TryGetPlayerInterop(out var playbackWrapper) && playbackWrapper.CanChangeVideoRate);
|
|
_back.Available = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_forward.Available = _currentTrack == null ? _playlistCurrent != null : _playlistCurrent != null && !_currentTrack.IsVideo;
|
|
_back.Available = _playlistCurrent != null && _playlistCurrent.CanRetreat;
|
|
}
|
|
UpdateTaskbarPlayer();
|
|
}
|
|
|
|
public static string FormatDuration(float seconds, bool prefixWithNegative) => Shell.TimeSpanToString(new TimeSpan(0, 0, (int)seconds), prefixWithNegative);
|
|
|
|
public static string FormatDuration(float seconds) => FormatDuration(seconds, false);
|
|
|
|
[Conditional("DEBUG_TRANSPORT")]
|
|
private static void _DEBUG_Trace(string message, params object[] args)
|
|
{
|
|
}
|
|
|
|
[Conditional("DEBUG_TRANSPORT_PROPERTIES")]
|
|
private static void _DEBUG_TracePropChange(string name, object arg)
|
|
{
|
|
}
|
|
|
|
private struct SpectrumOutputConfig
|
|
{
|
|
public uint SourceId;
|
|
public uint NumBands;
|
|
public bool Frequency;
|
|
public bool Waveform;
|
|
public bool Stereo;
|
|
public bool IsConnected;
|
|
}
|
|
|
|
private enum PlayerState
|
|
{
|
|
Stopped,
|
|
Playing,
|
|
Paused,
|
|
}
|
|
}
|
|
}
|