Skip to content

Commit

Permalink
Implemented audio track selector
Browse files Browse the repository at this point in the history
Audio tracks can now be selected. By default it will select the first
actual audio track.
  • Loading branch information
TimGels committed Jun 25, 2023
1 parent 5ae0be5 commit b1e6d9e
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 5 deletions.
20 changes: 20 additions & 0 deletions OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,25 @@ public interface IMediaTrackService
/// </summary>
/// <returns>Array containing <see cref="TrackDescription"/>'s or empty when no tracks are available</returns>
TrackDescription[] GetEmbeddedSubtitleTracks();


/// <summary>
/// Get the preferred audio track. When no preferred audio track is found, a default will be returned.
/// <br />
/// <i>Note: This method should not be called on a LibVLC thread.
/// <see href="https://code.videolan.org/videolan/LibVLCSharp/-/blob/3.x/docs/best_practices.md#do-not-call-libvlc-from-a-libvlc-event-without-switching-thread-first">Here</see> is more info on why.</i>
/// </summary>
/// <returns><see cref="TrackDescription"/> Preferred audio track or default</returns>
/// <exception cref="ServiceNotInitializedException"></exception>
TrackDescription GetPreferredAudioTrack();

/// <summary>
/// Get the embedded audio tracks in the media file.
/// <br />
/// <i>Note: This method should not be called on a LibVLC thread.
/// <see href="https://code.videolan.org/videolan/LibVLCSharp/-/blob/3.x/docs/best_practices.md#do-not-call-libvlc-from-a-libvlc-event-without-switching-thread-first">Here</see> is more info on why.</i>
/// </summary>
/// <returns>Array containing <see cref="TrackDescription"/>'s or empty when no tracks are available</returns>
TrackDescription[] GetEmbeddedAudioTracks();
}
}
22 changes: 21 additions & 1 deletion OneDrive-Cloud-Player/Services/MediaTrackService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public TrackDescription GetPreferredSubtitleTrack()
CheckInitializationState();
TrackDescription[] subtitleTracks = _mediaPlayer.SpuDescription;

if (_mediaPlayer.SpuCount <= 0)
if (_mediaPlayer.SpuCount <= 1)
{
return default;
}
Expand All @@ -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;
}
}
}
55 changes: 54 additions & 1 deletion OneDrive-Cloud-Player/ViewModels/VideoPlayerPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,31 @@ public ObservableCollection<TrackDescription> SubtitleTracks
}
}

private TrackDescription _selectedAudioTrack;

public TrackDescription SelectedAudioTrack
{
get { return _selectedAudioTrack; }
set
{
_selectedAudioTrack = value;
SetAudioTrackTrackById(value.Id);
OnPropertyChanged();
}
}

private ObservableCollection<TrackDescription> _audioTracks = new ObservableCollection<TrackDescription>();

public ObservableCollection<TrackDescription> AudioTracks
{
get { return _audioTracks; }
set
{
_audioTracks = value;
OnPropertyChanged();
}
}

/// <summary>
/// Gets the media player
/// </summary>
Expand Down Expand Up @@ -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)
Expand All @@ -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;
});
}

Expand Down Expand Up @@ -510,6 +554,15 @@ private void SetSubtitleTrackById(int subtitleTrackId)
_mediaPlayer.SetSpu(subtitleTrackId);
}

/// <summary>
/// Set the subtitle track used in the <see cref="_mediaPlayer"/> by id.
/// </summary>
/// <param name="subtitleTrackId">Subtitle track id</param>
private void SetAudioTrackTrackById(int audioTrackId)
{
_mediaPlayer.SetAudioTrack(audioTrackId);
}

private void SetMediaVolume(int volumeLevel)
{
if (_mediaPlayer is null)
Expand Down
17 changes: 15 additions & 2 deletions OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,21 @@
</StackPanel>
<TextBlock Text="No Tracks"
FontStyle="Italic"
Visibility="Visible">

Visibility="{x:Bind Path=IsAudioAvailable, Converter={StaticResource InvertBoolToVisibilityConverter}, Mode=OneWay}">
</TextBlock>
<ListView Visibility="{x:Bind Path=IsAudioAvailable, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"
ItemsSource="{x:Bind AudioTracks, Mode=OneWay}"
SelectedItem="{Binding SelectedAudioTrack, Mode=TwoWay}"
ItemClick="AudioTrackListView_ItemClick"
Name="AudioTrackListView"
IsItemClickEnabled="True">
<ListView.ItemTemplate>
<DataTemplate x:DataType="libvlcSharp:TrackDescription">
<StackPanel>
<TextBlock Text="{x:Bind Name}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</UserControl>
93 changes: 93 additions & 0 deletions OneDrive-Cloud-Player/Views/SubtitleAudioTrackSelector.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,38 @@ public bool IsSubtitlesAvailable
}
}

private bool _isAudioAvailable;

public bool IsAudioAvailable
{
get { return _isAudioAvailable; }
set
{
_isAudioAvailable = value;
OnPropertyChanged(nameof(IsAudioAvailable));
}
}

public ObservableCollection<TrackDescription> AudioTracks
{
get { return (ObservableCollection<TrackDescription>)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<TrackDescription>), 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<TrackDescription> SubtitleTracks
{
get { return (ObservableCollection<TrackDescription>)GetValue(SubtitleTracksProperty); }
Expand All @@ -49,6 +81,56 @@ public SubtitleAudioTrackSelector()
InitializeComponent();
}

/// <summary>
/// Callback for AudioTracksProperty property changes.
/// </summary>
/// <param name="dependencyObject"></param>
/// <param name="eventArgs"></param>
private static void AudioTracksPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
{
SubtitleAudioTrackSelector subtitleAudioTrackSelector = (SubtitleAudioTrackSelector)dependencyObject;
if (((ObservableCollection<TrackDescription>)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;
}
}

/// <summary>
/// Hnadling changes that occur inside the audio tracks collection (i.e. added or removed).
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
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;
}

/// <summary>
/// Callback for SubtitleTracksProperty property changes.
/// </summary>
Expand Down Expand Up @@ -110,6 +192,17 @@ private void SubtitleTrackListView_ItemClick(object sender, ItemClickEventArgs e
ItemClicked?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// Invoke the custom <see cref="ItemClicked"/> event when an audio item in the listview is clicked.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AudioTrackListView_ItemClick(object sender, ItemClickEventArgs e)
{
Debug.WriteLine("Audio item clicked from listview.");
ItemClicked?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// When called, notifies the UI that the property value has changed.
/// </summary>
Expand Down
4 changes: 3 additions & 1 deletion OneDrive-Cloud-Player/Views/VideoPlayerPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,9 @@
<!--Audio subtitle track selector-->
<local:SubtitleAudioTrackSelector SubtitleTracks="{Binding SubtitleTracks, Mode=OneWay}"
SelectedSubtitleTrack="{Binding SelectedSubtitleTrack, Mode=TwoWay}"
ItemClicked="SubtitleAudioTrackSelector_ItemClicked">
ItemClicked="SubtitleAudioTrackSelector_ItemClicked"
SelectedAudioTrack="{Binding SelectedAudioTrack, Mode=TwoWay}"
AudioTracks="{Binding AudioTracks, Mode=OneWay}">
</local:SubtitleAudioTrackSelector>
</Flyout>
</Button.Flyout>
Expand Down

0 comments on commit b1e6d9e

Please sign in to comment.