Repository: unoplatform/uno
File: src/AddIns/Uno.UI.MediaPlayer.Skia.X11/SharedMediaPlayerExtension.cs (C#)

Code before:
		Player = player;
		_mediaPlayerToExtension.TryAdd(player, this);

		// It's important not to let libVLC's media player grab a strong reference to this object,
		// otherwise neither will ever get collected. It seems like libVLC's media player is never
		// collected until explicitly disposed. The lifetime of X11mediaPlayerExtension is similar
		// to that of its owning MediaPlayer, which is part of the public API and  has an indefinite
		// lifetime. We rely on the GC to determine when it's time to end this object's lifetime,
		// and it turn, dispose of libVLC's media player handle as well.
		var weakRef = new WeakReference<SharedMediaPlayerExtension>(this);

		VlcPlayer.LengthChanged += (o, a) => weakRef.GetTarget()?.OnLengthChange(o, a);
		VlcPlayer.EndReached += (o, a) => weakRef.GetTarget()?.OnEndReached(o, a);
		VlcPlayer.EncounteredError += (o, a) => weakRef.GetTarget()?.OnEncounteredError(o, a);
		VlcPlayer.Playing += (o, a) => weakRef.GetTarget()?.OnPlaying(o, a);
		VlcPlayer.Buffering += (o, a) => weakRef.GetTarget()?.OnBuffering(o, a);
		VlcPlayer.Paused += (o, a) => weakRef.GetTarget()?.OnPaused(o, a);
		VlcPlayer.TimeChanged += (o, a) => weakRef.GetTarget()?.OnTimeChanged(o, a); // PositionChanged fires way too frequently (probably every frame). We use TimeChanged instead.
		VlcPlayer.VolumeChanged += (o, a) => weakRef.GetTarget()?.OnVlcVolumeChanged(o, a);

		if (PlatformHelper.IsWindows)
		{
			// Update the player volume from VLC volume, since all VLC instances have
			// shared volume control on Windows.
			OnVlcVolumeChanged(null, null!);
		}

		// We need to start a timer to update the playback state, since libVLC doesn't
		// provide a way to get the end of buffering without polling.
		var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) };
		EventHandler<object> timerOnTick = (_, _) => weakRef.GetTarget()?.OnTick();
		timer.Tick += timerOnTick;
		_timerDisposable = Disposable.Create(() => timer.Tick -= timerOnTick);
		timer.Start();
	}


	~SharedMediaPlayerExtension()
	{
		Dispose();
	}

	internal static SharedMediaPlayerExtension? GetByMediaPlayer(Windows.Media.Playback.MediaPlayer player) => _mediaPlayerToExtension.TryGetValue(player, out var ext) ? ext : null;

	public IMediaPlayerEventsExtension? Events { get; set; }

	public double PlaybackRate
	{
		get => VlcPlayer.Rate;
		set => VlcPlayer.SetRate((float)value);
	}

Review comment (bug):
You have ignored this comment. When I tested on my side, your subscription crashes the app when a debugger is attached. We can stick with the original logic as it was, unless there's a problem with it.

Code after:
		VlcPlayer = new LibVLCSharp.Shared.MediaPlayer(_vlc) { EnableMouseInput = false, EnableKeyInput = false };
		Player = player;
		_mediaPlayerToExtension.TryAdd(player, this);

		// It's important not to let libVLC's media player grab a strong reference to this object,
		// otherwise neither will ever get collected. It seems like libVLC's media player is never
		// collected until explicitly disposed. The lifetime of X11mediaPlayerExtension is similar
		// to that of its owning MediaPlayer, which is part of the public API and  has an indefinite
		// lifetime. We rely on the GC to determine when it's time to end this object's lifetime,
		// and it turn, dispose of libVLC's media player handle as well.
		var weakRef = new WeakReference<SharedMediaPlayerExtension>(this);

		VlcPlayer.LengthChanged += (o, a) => weakRef.GetTarget()?.OnLengthChange(o, a);
		VlcPlayer.EndReached += (o, a) => weakRef.GetTarget()?.OnEndReached(o, a);
		VlcPlayer.EncounteredError += (o, a) => weakRef.GetTarget()?.OnEncounteredError(o, a);
		VlcPlayer.Playing += (o, a) => weakRef.GetTarget()?.OnPlaying(o, a);
		VlcPlayer.Buffering += (o, a) => weakRef.GetTarget()?.OnBuffering(o, a);
		VlcPlayer.Paused += (o, a) => weakRef.GetTarget()?.OnPaused(o, a);
		VlcPlayer.TimeChanged += (o, a) => weakRef.GetTarget()?.OnTimeChanged(o, a); // PositionChanged fires way too frequently (probably every frame). We use TimeChanged instead.

		//VlcPlayer.VolumeChanged += (o, a) => weakRef.GetTarget()?.OnVlcVolumeChanged(o, a);
		// We need to start a timer to update the playback state, since libVLC doesn't
		// provide a way to get the end of buffering without polling.
		// Also, attaching to VolumeChanged with debugger attached causes crashes on some systems,
		// so we poll the volume on the timer as well.
		var timer = new System.Timers.Timer(16);
		ElapsedEventHandler timerOnTick = (_, _) => weakRef.GetTarget()?.OnTick();
		timer.Elapsed += timerOnTick;
		_timerDisposable = timer;
		timer.Start();
	}


	~SharedMediaPlayerExtension()
	{
		Dispose();
	}

	internal static SharedMediaPlayerExtension? GetByMediaPlayer(Windows.Media.Playback.MediaPlayer player) => _mediaPlayerToExtension.TryGetValue(player, out var ext) ? ext : null;

	public IMediaPlayerEventsExtension? Events { get; set; }

	public double PlaybackRate
	{
		get => VlcPlayer.Rate;
		set => VlcPlayer.SetRate((float)value);
	}

	public bool IsLoopingEnabled { get; set; }

	public bool IsLoopingAllEnabled { get; set; }