Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Screenbox.Core/Enums/PlaybackBackendType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Screenbox.Core.Enums;

public enum PlaybackBackendType
{
Auto,
Ffmpeg,
LibVlc,
}
21 changes: 7 additions & 14 deletions Screenbox.Core/Factories/MediaViewModelFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using LibVLCSharp.Shared;
using Screenbox.Core.Playback;
using Screenbox.Core.Services;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -11,34 +10,28 @@ namespace Screenbox.Core.Factories
{
public sealed class MediaViewModelFactory
{
private readonly LibVlcService _libVlcService;
private readonly Dictionary<string, WeakReference<MediaViewModel>> _references = new();
private int _referencesCleanUpThreshold = 1000;

public MediaViewModelFactory(LibVlcService libVlcService)
{
_libVlcService = libVlcService;
}

public MediaViewModel GetTransient(StorageFile file)
{
return new MediaViewModel(_libVlcService, file);
return new MediaViewModel(file);
}

public MediaViewModel GetTransient(Uri uri)
{
return new MediaViewModel(_libVlcService, uri);
return new MediaViewModel(uri);
}

public MediaViewModel GetTransient(Media media)
{
if (!Uri.TryCreate(media.Mrl, UriKind.Absolute, out Uri uri))
return new MediaViewModel(_libVlcService, media);
return new MediaViewModel(media);

// Prefer URI source for easier clean up
MediaViewModel vm = new(_libVlcService, uri)
MediaViewModel vm = new(uri)
{
Item = new Lazy<PlaybackItem>(new PlaybackItem(media, media))
Item = new VlcPlaybackItem(media, media)
};

if (media.Meta(MetadataType.Title) is { } name)
Expand All @@ -64,7 +57,7 @@ public MediaViewModel GetSingleton(StorageFile file)


// No existing reference, create new instance
instance = new MediaViewModel(_libVlcService, file);
instance = new MediaViewModel(file);
if (!string.IsNullOrEmpty(id))
{
_references[id] = new WeakReference<MediaViewModel>(instance);
Expand All @@ -81,7 +74,7 @@ public MediaViewModel GetSingleton(Uri uri)
reference.TryGetTarget(out MediaViewModel instance)) return instance;

// No existing reference, create new instance
instance = new MediaViewModel(_libVlcService, uri);
instance = new MediaViewModel(uri);
if (!string.IsNullOrEmpty(id))
{
_references[id] = new WeakReference<MediaViewModel>(instance);
Expand Down
15 changes: 7 additions & 8 deletions Screenbox.Core/Playback/IMediaPlayer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#nullable enable

using Screenbox.Core.Events;
using System;
using Windows.Devices.Enumeration;
using Windows.Foundation;
Expand All @@ -17,17 +16,17 @@ public interface IMediaPlayer
event TypedEventHandler<IMediaPlayer, EventArgs>? MediaOpened;
event TypedEventHandler<IMediaPlayer, EventArgs>? IsMutedChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? VolumeChanged;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<PlaybackItem?>>? PlaybackItemChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? PlaybackItemChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? BufferingProgressChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? BufferingStarted;
event TypedEventHandler<IMediaPlayer, EventArgs>? BufferingEnded;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<TimeSpan>>? NaturalDurationChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? NaturalDurationChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? NaturalVideoSizeChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? CanSeekChanged;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<TimeSpan>>? PositionChanged;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<ChapterCue?>>? ChapterChanged;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<MediaPlaybackState>>? PlaybackStateChanged;
event TypedEventHandler<IMediaPlayer, ValueChangedEventArgs<double>>? PlaybackRateChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? PositionChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? ChapterChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? PlaybackStateChanged;
event TypedEventHandler<IMediaPlayer, EventArgs>? PlaybackRateChanged;

bool CanPause { get; }
bool CanSeek { get; }
Expand All @@ -44,7 +43,7 @@ public interface IMediaPlayer
double PlaybackRate { get; set; }
Rect NormalizedSourceRect { get; set; }
double Volume { get; set; }
public PlaybackItem? PlaybackItem { get; set; }
public IPlaybackItem? PlaybackItem { get; set; }

void Close();
void Play();
Expand Down
12 changes: 12 additions & 0 deletions Screenbox.Core/Playback/IPlaybackItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Screenbox.Core.Playback;
public interface IPlaybackItem
{
object OriginalSource { get; }
PlaybackAudioTrackList AudioTracks { get; }
PlaybackVideoTrackList VideoTracks { get; }
PlaybackSubtitleTrackList SubtitleTracks { get; }
PlaybackChapterList Chapters { get; }
TimeSpan StartTime { get; set; }
}
37 changes: 34 additions & 3 deletions Screenbox.Core/Playback/PlaybackChapterList.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
using LibVLCSharp.Shared.Structures;
#nullable enable

using LibVLCSharp.Shared.Structures;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Media.Core;
using Windows.Media.Playback;

namespace Screenbox.Core.Playback
{
public sealed class PlaybackChapterList : ReadOnlyCollection<ChapterCue>
{
private readonly List<ChapterCue> _chapters;
private readonly PlaybackItem _item;
private readonly IPlaybackItem _item;
public TimedMetadataTrack? ChapterTrack { get; }

internal PlaybackChapterList(IPlaybackItem item) : base(new List<ChapterCue>())
{
_item = item;
_chapters = (List<ChapterCue>)Items;
}

internal PlaybackChapterList(PlaybackItem item) : base(new List<ChapterCue>())
internal PlaybackChapterList(MediaPlaybackTimedMetadataTrackList source, IPlaybackItem item) : base(new List<ChapterCue>())
{
_item = item;
_chapters = (List<ChapterCue>)Items;

foreach (TimedMetadataTrack metadataTrack in source)
{
if (metadataTrack.TimedMetadataKind is not TimedMetadataKind.Chapter) continue;
ChapterTrack = metadataTrack;
foreach (IMediaCue cue in metadataTrack.Cues)
{
if (cue is ChapterCue chapterCue)
_chapters.Add(chapterCue);
else
_chapters.Add(new ChapterCue
{
Title = string.Empty,
Id = cue.Id,
Duration = cue.Duration,
StartTime = cue.StartTime
});
}

break;
}
}

public void Load(IMediaPlayer player)
Expand Down
61 changes: 49 additions & 12 deletions Screenbox.Core/Playback/PlaybackSubtitleTrackList.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
using LibVLCSharp.Shared;
#nullable enable

using LibVLCSharp.Shared;
using System.Collections.Generic;
using System.Linq;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;

namespace Screenbox.Core.Playback
{
public sealed class PlaybackSubtitleTrackList : SingleSelectTrackList<SubtitleTrack>
{
private readonly Media _media;
private readonly Media? _media;
private readonly MediaPlaybackTimedMetadataTrackList? _source;
private readonly List<LazySubtitleTrack> _pendingSubtitleTracks;

private class LazySubtitleTrack
Expand Down Expand Up @@ -36,26 +40,59 @@ public PlaybackSubtitleTrackList(Media media)
{
_pendingSubtitleTracks = new List<LazySubtitleTrack>();
_media = media;
if (_media.Tracks.Length > 0)
if (media.Tracks.Length > 0)
{
AddVlcMediaTracks(_media.Tracks);
AddVlcMediaTracks(media.Tracks);
}
else
{
_media.ParsedChanged += Media_ParsedChanged;
media.ParsedChanged += Media_ParsedChanged;
}

SelectedIndexChanged += OnSelectedIndexChanged;
}

private void OnSelectedIndexChanged(ISingleSelectMediaTrackList sender, object args)
public PlaybackSubtitleTrackList(MediaPlaybackTimedMetadataTrackList source)
{
if (SelectedIndex >= 0 && TrackList[SelectedIndex] is { } selectedTrack &&
_pendingSubtitleTracks.FirstOrDefault(x => ReferenceEquals(x.Track, selectedTrack)) is { } lazyTrack &&
(selectedTrack.VlcSpu == -1 || lazyTrack.Player.VlcPlayer.SpuCount < selectedTrack.VlcSpu))
_pendingSubtitleTracks = new List<LazySubtitleTrack>();
_source = source;
foreach (TimedMetadataTrack metadataTrack in source)
{
selectedTrack.VlcSpu = -1;
lazyTrack.Player.AddSubtitle(lazyTrack.File, true);
if (metadataTrack.TimedMetadataKind is TimedMetadataKind.Caption or TimedMetadataKind.Subtitle
or TimedMetadataKind.ImageSubtitle)
{
TrackList.Add(new SubtitleTrack(metadataTrack));
}
}

SelectedIndexChanged += OnSelectedIndexChanged;
}

private void OnSelectedIndexChanged(ISingleSelectMediaTrackList sender, object? args)
{
if (_media != null)
{
if (SelectedIndex >= 0 && TrackList[SelectedIndex] is { } selectedTrack &&
_pendingSubtitleTracks.FirstOrDefault(x => ReferenceEquals(x.Track, selectedTrack)) is { } lazyTrack &&
(selectedTrack.VlcSpu == -1 || lazyTrack.Player.VlcPlayer.SpuCount < selectedTrack.VlcSpu))
{
selectedTrack.VlcSpu = -1;
lazyTrack.Player.AddSubtitle(lazyTrack.File, true);
}
}
else if (_source != null)
{
if (sender.SelectedIndex == -1)
{
for (uint i = 0; i < _source.Count; i++)
{
_source.SetPresentationMode(i, TimedMetadataTrackPresentationMode.Disabled);
}
}
else
{
_source.SetPresentationMode((uint)sender.SelectedIndex, TimedMetadataTrackPresentationMode.PlatformPresented);
}
}
}

Expand All @@ -82,7 +119,7 @@ internal void NotifyTrackAdded(int trackId)

private void Media_ParsedChanged(object sender, MediaParsedChangedEventArgs e)
{
if (e.ParsedStatus != MediaParsedStatus.Done) return;
if (_media == null || e.ParsedStatus != MediaParsedStatus.Done) return;
_media.ParsedChanged -= Media_ParsedChanged;
AddVlcMediaTracks(_media.Tracks);
}
Expand Down
35 changes: 30 additions & 5 deletions Screenbox.Core/Playback/PlaybackVideoTrackList.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,54 @@
using LibVLCSharp.Shared;
#nullable enable

using LibVLCSharp.Shared;
using Windows.Media.Core;
using Windows.Media.Playback;

namespace Screenbox.Core.Playback
{
public sealed class PlaybackVideoTrackList : SingleSelectTrackList<VideoTrack>
{
private readonly Media _media;
private readonly Media? _media;
private readonly MediaPlaybackVideoTrackList? _source;

public PlaybackVideoTrackList(Media media)
{
_media = media;
if (_media.Tracks.Length > 0)
if (media.Tracks.Length > 0)
{
AddVlcMediaTracks(_media.Tracks);
}
else
{
_media.ParsedChanged += Media_ParsedChanged;
media.ParsedChanged += Media_ParsedChanged;
}

SelectedIndex = 0;
}

public PlaybackVideoTrackList(MediaPlaybackVideoTrackList source)
{
_source = source;
SelectedIndex = source.SelectedIndex;
source.SelectedIndexChanged += (sender, args) => SelectedIndex = sender.SelectedIndex;
foreach (Windows.Media.Core.VideoTrack videoTrack in source)
{
TrackList.Add(new VideoTrack(videoTrack));
}

SelectedIndexChanged += OnSelectedIndexChanged;
}

private void OnSelectedIndexChanged(ISingleSelectMediaTrackList sender, object? args)
{
// Only update for Windows track list. VLC track list is handled by the player.
if (_source == null || _source.SelectedIndex == sender.SelectedIndex) return;
_source.SelectedIndex = sender.SelectedIndex;
}

private void Media_ParsedChanged(object sender, MediaParsedChangedEventArgs e)
{
if (e.ParsedStatus != MediaParsedStatus.Done) return;
if (_media == null || e.ParsedStatus != MediaParsedStatus.Done) return;
_media.ParsedChanged -= Media_ParsedChanged;
AddVlcMediaTracks(_media.Tracks);
}
Expand Down
Loading