Skip to content

Commit

Permalink
Added Subtitle Track selector
Browse files Browse the repository at this point in the history
Implemented the ability to select a subtitle track on the video player
page.

Added a SubtitleAudioTrackSelector UserControl. This UserControl hosts
the subtitle tracks and will also host the audio tracks from which a
user can select a track.

Introduced a MediaTrackService which can be injected to provider some
methods. Methods it provide are at this moment the retrieval of subtitle
tracks and the preferred subtitle track. Due to LibVLC not being able to
be injected by a dependency injector due to several reasons, this
service needs to be initialized by calling the initialize method and
providing it with a reference to a (initialized!) MediaPlayer instance. This service can be expanded to
perform more general MediaPlayer tasks as well in the future if need be.

Added a ValueConverterGroup enabling the chaining of value converters.

Added a custom exception which gets thrown when one of the the MediaTrackService
methods get called without the service being initialized.

Added MediaTrackService to the dependency Injector container in App.xaml.cs.

Replaced public mediaplayer property calls with private.
  • Loading branch information
TimGels committed Mar 25, 2023
1 parent c8f4558 commit e19ef3e
Show file tree
Hide file tree
Showing 11 changed files with 500 additions and 43 deletions.
2 changes: 2 additions & 0 deletions OneDrive-Cloud-Player/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Client;
using OneDrive_Cloud_Player.Models.GraphData;
using OneDrive_Cloud_Player.Models.Interfaces;
using OneDrive_Cloud_Player.Services;
using OneDrive_Cloud_Player.Views;
using System;
Expand Down Expand Up @@ -83,6 +84,7 @@ IServiceProvider ConfigureDependencyInjection()
var serviceCollection = new ServiceCollection();

// Add services for dependency injection here.
serviceCollection.AddTransient<IMediaTrackService, MediaTrackService>();

return serviceCollection.BuildServiceProvider();
}
Expand Down
37 changes: 37 additions & 0 deletions OneDrive-Cloud-Player/Models/Interfaces/IMediaTrackService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using LibVLCSharp.Shared;
using LibVLCSharp.Shared.Structures;
using System.Threading.Tasks;

namespace OneDrive_Cloud_Player.Models.Interfaces
{
public interface IMediaTrackService
{
/// <summary>
/// Needs to be called before using the service and before subscribing to other libvlcsharp events.<br />
/// Due to LibVLCSharp's inability for the MediaPlayer to be put into a service, we need to get a reference to the MediaPlayer ourselves instead.<br />
/// Initialize this service by parsing the MediaPlayer object reference for its media tracks.
/// </summary>
/// <param name="mediaPlayer"></param>
/// <returns></returns>
IMediaTrackService Initialize(ref MediaPlayer mediaPlayer);

/// <summary>
/// Get the preferred subtitle track. When no preferred subtitle 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 subtitle track or default</returns>
/// <exception cref="ServiceNotInitializedException"></exception>
TrackDescription GetPreferredSubtitleTrack();

/// <summary>
/// Get the embedded subtitle 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[] GetEmbeddedSubtitleTracks();
}
}
16 changes: 16 additions & 0 deletions OneDrive-Cloud-Player/OneDrive-Cloud-Player.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,18 @@
<Compile Include="Models\GraphData\CachedDrive.cs" />
<Compile Include="Models\GraphData\CachedDriveItem.cs" />
<Compile Include="Models\GraphData\OneDriveCache.cs" />
<Compile Include="Models\Interfaces\IMediaTrackService.cs" />
<Compile Include="Models\Interfaces\INavigable.cs" />
<Compile Include="Models\MediaWrapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\Converters\MainPageExplorerConverter.cs" />
<Compile Include="Services\Converters\MediaTimeConverter.cs" />
<Compile Include="Services\Converters\ValueConverterGroup.cs" />
<Compile Include="Services\Helpers\CacheHelper.cs" />
<Compile Include="Services\Helpers\GraphHelper.cs" />
<Compile Include="Services\Helpers\GraphAuthHelper.cs" />
<Compile Include="Services\NavigationService.cs" />
<Compile Include="Services\MediaTrackService.cs" />
<Compile Include="Services\Utilities\JsonHandler.cs" />
<Compile Include="ViewModels\LoginPageViewModel.cs" />
<Compile Include="ViewModels\MainPageViewModel.cs" />
Expand All @@ -154,6 +157,9 @@
<Compile Include="Views\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SubtitleAudioTrackSelector.xaml.cs">
<DependentUpon>SubtitleAudioTrackSelector.xaml</DependentUpon>
</Compile>
<Compile Include="Views\VideoPlayerPage.xaml.cs">
<DependentUpon>VideoPlayerPage.xaml</DependentUpon>
</Compile>
Expand Down Expand Up @@ -250,6 +256,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\SubtitleAudioTrackSelector.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\VideoPlayerPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -277,6 +287,12 @@
<PackageReference Include="Microsoft.Toolkit.Mvvm">
<Version>7.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI">
<Version>7.1.3</Version>
</PackageReference>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.8.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
<Version>2.0.1</Version>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace OneDrive_Cloud_Player.Services.Converters
class MediaTimeConverter : IValueConverter
{
object IValueConverter.Convert(object timeMs, Type targetType, object parameter, string language)
{
{
TimeSpan timeSpan = TimeSpan.FromMilliseconds(Convert.ToDouble(timeMs));
return timeSpan.ToString(@"hh\:mm\:ss");
}
Expand Down
20 changes: 20 additions & 0 deletions OneDrive-Cloud-Player/Services/Converters/ValueConverterGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Data;

namespace OneDrive_Cloud_Player.Services.Converters
{
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, language));
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
90 changes: 90 additions & 0 deletions OneDrive-Cloud-Player/Services/MediaTrackService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using LibVLCSharp.Shared;
using LibVLCSharp.Shared.Structures;
using OneDrive_Cloud_Player.Models.Interfaces;
using System;
using System.Diagnostics;
using System.Linq;
using Windows.Storage;

namespace OneDrive_Cloud_Player.Services
{
/// <summary>
/// Manage the media tracks of the libvlcsharp media player instance.
/// </summary>
public class MediaTrackService : IMediaTrackService
{
private MediaPlayer _mediaPlayer;
private bool _isInitialized = false;
private readonly ApplicationDataContainer _userSettings;

public MediaTrackService()
{
_userSettings = ApplicationData.Current.LocalSettings;
}

/// <summary>
/// Due to LibVLCSharp's inability for the MediaPlayer to be put into a service, we need to get a reference to the MediaPlayer ourselves instead.
/// Initialize this service by parsing the MediaPlayer object reference for its media tracks.
/// </summary>
public IMediaTrackService Initialize(ref MediaPlayer mediaPlayer)
{
if (_isInitialized)
{
throw new InvalidOperationException("Service already initialized!");
}

Debug.WriteLine(String.Format("initializing {0}", GetType().Name));
_mediaPlayer = mediaPlayer;
_isInitialized = true;
return this;
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <exception cref="InvalidOperationException"></exception>
private void CheckInitializationState()
{
if (!_isInitialized)
{
throw new InvalidOperationException("Service not initialized!");
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns> <inheritdoc/></returns>
public TrackDescription GetPreferredSubtitleTrack()
{
CheckInitializationState();
TrackDescription[] subtitleTracks = _mediaPlayer.SpuDescription;

if (_mediaPlayer.SpuCount <= 0)
{
return default;
}

// Return default track subtitle (when possible, like in mkv) depending on the user setting.
if ((bool)_userSettings.Values["ShowDefaultSubtitles"])
{
return subtitleTracks.First(subtitleTrack => subtitleTrack.Id == _mediaPlayer.Spu);
}
else
{
// Return the "disabled" indicator track positioned at index 0.
return _mediaPlayer.SpuDescription.ElementAt(0);
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns><inheritdoc/></returns>
public TrackDescription[] GetEmbeddedSubtitleTracks()
{
CheckInitializationState();
return _mediaPlayer.SpuDescription;
}
}
}
Loading

0 comments on commit e19ef3e

Please sign in to comment.