diff --git a/OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs b/OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs
index 0c27a88..b4f2737 100644
--- a/OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs
+++ b/OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs
@@ -33,5 +33,25 @@ public interface IMediaTrackService
///
/// Array containing 's or empty when no tracks are available
TrackDescription[] GetEmbeddedSubtitleTracks();
+
+
+ ///
+ /// Get the preferred audio track. When no preferred audio track is found, a default will be returned.
+ ///
+ /// Note: This method should not be called on a LibVLC thread.
+ /// Here is more info on why.
+ ///
+ /// Preferred audio track or default
+ ///
+ TrackDescription GetPreferredAudioTrack();
+
+ ///
+ /// Get the embedded audio tracks in the media file.
+ ///
+ /// Note: This method should not be called on a LibVLC thread.
+ /// Here is more info on why.
+ ///
+ /// Array containing 's or empty when no tracks are available
+ TrackDescription[] GetEmbeddedAudioTracks();
}
}
diff --git a/OneDrive-Cloud-Player/Services/MediaTrackService.cs b/OneDrive-Cloud-Player/Services/MediaTrackService.cs
index bc51d86..5a1799f 100644
--- a/OneDrive-Cloud-Player/Services/MediaTrackService.cs
+++ b/OneDrive-Cloud-Player/Services/MediaTrackService.cs
@@ -60,7 +60,7 @@ public TrackDescription GetPreferredSubtitleTrack()
CheckInitializationState();
TrackDescription[] subtitleTracks = _mediaPlayer.SpuDescription;
- if (_mediaPlayer.SpuCount <= 0)
+ if (_mediaPlayer.SpuCount <= 1)
{
return default;
}
@@ -86,5 +86,25 @@ public TrackDescription[] GetEmbeddedSubtitleTracks()
CheckInitializationState();
return _mediaPlayer.SpuDescription;
}
+
+ public TrackDescription GetPreferredAudioTrack()
+ {
+ CheckInitializationState();
+ TrackDescription[] audioTracks = _mediaPlayer.AudioTrackDescription;
+
+ if (_mediaPlayer.AudioTrackCount <= 1)
+ {
+ return default;
+ }
+
+ // Return the first actual audio track.
+ return _mediaPlayer.AudioTrackDescription.ElementAt(1);
+ }
+
+ public TrackDescription[] GetEmbeddedAudioTracks()
+ {
+ CheckInitializationState();
+ return _mediaPlayer.AudioTrackDescription;
+ }
}
}
diff --git a/OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs b/OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs
index 3ce1eb1..2f17c59 100644
--- a/OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs
+++ b/OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs
@@ -237,6 +237,31 @@ public ObservableCollection SubtitleTracks
}
}
+ private TrackDescription _selectedAudioTrack;
+
+ public TrackDescription SelectedAudioTrack
+ {
+ get { return _selectedAudioTrack; }
+ set
+ {
+ _selectedAudioTrack = value;
+ SetAudioTrackTrackById(value.Id);
+ OnPropertyChanged();
+ }
+ }
+
+ private ObservableCollection _audioTracks = new ObservableCollection();
+
+ public ObservableCollection AudioTracks
+ {
+ get { return _audioTracks; }
+ set
+ {
+ _audioTracks = value;
+ OnPropertyChanged();
+ }
+ }
+
///
/// Gets the media player
///
@@ -350,11 +375,16 @@ private async void MediaPlayer_Playing(object sender, EventArgs e)
{
TrackDescription[] subtitleTracks = null;
TrackDescription selectedSubtitleTrack = default;
+ TrackDescription[] audioTracks = null;
+ TrackDescription selectedAudioTrack = default;
await Task.Run(() =>
{
subtitleTracks = _mediaTrackService.GetEmbeddedSubtitleTracks();
selectedSubtitleTrack = _mediaTrackService.GetPreferredSubtitleTrack();
+ audioTracks = _mediaTrackService.GetEmbeddedAudioTracks();
+ selectedAudioTrack = _mediaTrackService.GetPreferredAudioTrack();
+
});
if (_isFirstPlaying)
@@ -372,19 +402,33 @@ await Task.Run(() =>
// Retrieve the same selected subtitle track again as the one that was used with the former subtitle tracks collection.
selectedSubtitleTrack = subtitleTracks.FirstOrDefault(subtitleTrack => subtitleTrack.Id == _selectedSubtitleTrack.Id);
}
+
+ // Check if there is a selected audio track, for re-selection, to begin with.
+ if (_selectedAudioTrack.Id != 0 && _selectedAudioTrack.Name != null)
+ {
+ // Retrieve the same selected audio track again as the one that was used with the former audio tracks collection.
+ selectedAudioTrack = audioTracks.FirstOrDefault(audioTrack => audioTrack.Id == _selectedAudioTrack.Id);
+ }
}
await App.Current.UIDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// Clear collection as we want to refill it with updated tracks from the new media source.
SubtitleTracks.Clear();
+ AudioTracks.Clear();
foreach (TrackDescription subtitleTrack in subtitleTracks)
{
SubtitleTracks.Add(subtitleTrack);
}
- // Select the correct subtitle track.
+ foreach (TrackDescription audioTrack in audioTracks)
+ {
+ AudioTracks.Add(audioTrack);
+ }
+
+ // Select the correct track.
SelectedSubtitleTrack = selectedSubtitleTrack;
+ SelectedAudioTrack = selectedAudioTrack;
});
}
@@ -507,6 +551,15 @@ private void SetSubtitleTrackById(int subtitleTrackId)
_mediaPlayer.SetSpu(subtitleTrackId);
}
+ ///
+ /// Set the subtitle track used in the by id.
+ ///
+ /// Subtitle track id
+ private void SetAudioTrackTrackById(int audioTrackId)
+ {
+ _mediaPlayer.SetAudioTrack(audioTrackId);
+ }
+
private void SetMediaVolume(int volumeLevel)
{
if (_mediaPlayer is null)
diff --git a/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml b/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml
index 3cfbb05..87d2d5a 100644
--- a/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml
+++ b/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml
@@ -42,8 +42,21 @@
-
+ Visibility="{x:Bind Path=IsAudioAvailable, Converter={StaticResource InvertBoolToVisibilityConverter}, Mode=OneWay}">
+
+
+
+
+
+
+
+
+
diff --git a/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml.cs b/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml.cs
index 825f9f4..fcb23d4 100644
--- a/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml.cs
+++ b/OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml.cs
@@ -24,6 +24,38 @@ public bool IsSubtitlesAvailable
}
}
+ private bool _isAudioAvailable;
+
+ public bool IsAudioAvailable
+ {
+ get { return _isAudioAvailable; }
+ set
+ {
+ _isAudioAvailable = value;
+ OnPropertyChanged(nameof(IsAudioAvailable));
+ }
+ }
+
+ public ObservableCollection AudioTracks
+ {
+ get { return (ObservableCollection)GetValue(AudioTracksProperty); }
+ set { SetValue(AudioTracksProperty, value); }
+ }
+
+ // Using a DependencyProperty as the backing store for AudioTracks. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty AudioTracksProperty =
+ DependencyProperty.Register("AudioTracks", typeof(ObservableCollection), typeof(SubtitleAudioTrackSelector), new PropertyMetadata(null, AudioTracksPropertyChangedCallback));
+
+ public TrackDescription SelectedAudioTrack
+ {
+ get { return (TrackDescription)GetValue(SelectedAudioTrackProperty); }
+ set { SetValue(SelectedAudioTrackProperty, value); }
+ }
+
+ // Using a DependencyProperty as the backing store for SelectedAudioTrack. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty SelectedAudioTrackProperty =
+ DependencyProperty.Register("SelectedAudioTrack", typeof(TrackDescription), typeof(SubtitleAudioTrackSelector), new PropertyMetadata(null));
+
public ObservableCollection SubtitleTracks
{
get { return (ObservableCollection)GetValue(SubtitleTracksProperty); }
@@ -49,6 +81,56 @@ public SubtitleAudioTrackSelector()
InitializeComponent();
}
+ ///
+ /// Callback for AudioTracksProperty property changes.
+ ///
+ ///
+ ///
+ private static void AudioTracksPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
+ {
+ SubtitleAudioTrackSelector subtitleAudioTrackSelector = (SubtitleAudioTrackSelector)dependencyObject;
+ if (((ObservableCollection)eventArgs.NewValue).Count > 0)
+ {
+ subtitleAudioTrackSelector.IsAudioAvailable = true;
+ }
+
+ if (eventArgs.NewValue is INotifyCollectionChanged newCollection)
+ {
+ // Subscribe to changes inside the new collection.
+ newCollection.CollectionChanged += subtitleAudioTrackSelector.AudioTracks_CollectionChanged;
+ }
+
+ if (eventArgs.OldValue is INotifyCollectionChanged oldCollection)
+ {
+ // Unsubscribe to changes inside the old collection.
+ oldCollection.CollectionChanged -= subtitleAudioTrackSelector.AudioTracks_CollectionChanged;
+ }
+ }
+
+ ///
+ /// Hnadling changes that occur inside the audio tracks collection (i.e. added or removed).
+ ///
+ ///
+ ///
+ private void AudioTracks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ // Check for Default value.
+ if (e.NewItems == null)
+ {
+ IsAudioAvailable = false;
+ return;
+ }
+
+ // Check if there are subtitles.
+ if (e.NewItems.Count <= 0)
+ {
+ IsAudioAvailable = false;
+ return;
+ }
+
+ IsAudioAvailable = true;
+ }
+
///
/// Callback for SubtitleTracksProperty property changes.
///
@@ -110,6 +192,17 @@ private void SubtitleTrackListView_ItemClick(object sender, ItemClickEventArgs e
ItemClicked?.Invoke(this, EventArgs.Empty);
}
+ ///
+ /// Invoke the custom event when an audio item in the listview is clicked.
+ ///
+ ///
+ ///
+ private void AudioTrackListView_ItemClick(object sender, ItemClickEventArgs e)
+ {
+ Debug.WriteLine("Audio item clicked from listview.");
+ ItemClicked?.Invoke(this, EventArgs.Empty);
+ }
+
///
/// When called, notifies the UI that the property value has changed.
///
diff --git a/OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml b/OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml
index c41ab1b..3292884 100644
--- a/OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml
+++ b/OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml
@@ -484,7 +484,9 @@
+ ItemClicked="SubtitleAudioTrackSelector_ItemClicked"
+ SelectedAudioTrack="{Binding SelectedAudioTrack, Mode=TwoWay}"
+ AudioTracks="{Binding AudioTracks, Mode=OneWay}">