C# Avalonia没有内置的MediaElement,我们用LibVLC实现一个就行了。Avalonia官方实现的,要收费。配置文章里更新了。https://www.cnblogs.com/dalgleish/p/18967204

MediaElement类,已完善功能。之后会有专门一章来测试这个播放器代码,现在可以简单使用播放功能。

using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Threading;
using LibVLCSharp.Avalonia;
using LibVLCSharp.Shared;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Shares.Avalonia.CustomControls
{
    public class MediaElement : UserControl, IDisposable
    {
        private static LibVLC? libVLC;

        private VideoView? videoView;
        private MediaPlayer? mediaPlayer;

        private Media? currentMedia;
        private EventHandler<MediaParsedChangedEventArgs>? parsedChangedHandler;

        private Stream? currentStream;
        private StreamMediaInput? currentMediaInput;

        private bool isDisposed;
        private bool isStopping;

        private bool updatingTimeFromPlayer;
        private bool updatingMuteFromPlayer;

        private DispatcherTimer? seekDebounceTimer;
        private long pendingSeekTime;
        private bool hasPendingSeek;
        private bool isScrubbing;

        private bool stretchPending;

        private CancellationTokenSource? sourceCts;

        private bool isVideoDetached;

        private EventHandler<EventArgs>? pausedHandler;
        private EventHandler<EventArgs>? stoppedHandler;
        private EventHandler<EventArgs>? playingHandler;
        private EventHandler<MediaPlayerVolumeChangedEventArgs>? volumeChangedHandler;
        private EventHandler<MediaPlayerTimeChangedEventArgs>? timeChangedHandler;
        private EventHandler<MediaPlayerLengthChangedEventArgs>? lengthChangedHandler;
        private EventHandler<EventArgs>? mutedHandler;
        private EventHandler<EventArgs>? unmutedHandler;
        private EventHandler<MediaPlayerBufferingEventArgs>? bufferingHandler;
        private EventHandler<EventArgs>? endReachedHandler;

        public event EventHandler? Paused;
        public event EventHandler? Stopped;
        public event EventHandler? Playing;
        public event EventHandler? VolumeChanged;
        public event EventHandler? TimeChanged;
        public event EventHandler? LengthChanged;
        public event EventHandler? Muted;
        public event EventHandler? Unmuted;
        public event EventHandler? Buffering;
        public event EventHandler? MediaEnded;

        public static readonly StyledProperty<Uri> SourceProperty =
            AvaloniaProperty.Register<MediaElement, Uri>(nameof(Source));

        public Uri Source
        {
            get => GetValue(SourceProperty);
            set => SetValue(SourceProperty, value);
        }

        public static readonly StyledProperty<uint> VideoWidthProperty =
            AvaloniaProperty.Register<MediaElement, uint>(nameof(VideoWidth));

        public uint VideoWidth
        {
            get => GetValue(VideoWidthProperty);
            private set => SetValue(VideoWidthProperty, value);
        }

        public static readonly StyledProperty<uint> VideoHeightProperty =
            AvaloniaProperty.Register<MediaElement, uint>(nameof(VideoHeight));

        public uint VideoHeight
        {
            get => GetValue(VideoHeightProperty);
            private set => SetValue(VideoHeightProperty, value);
        }

        public static readonly StyledProperty<bool> AutoPlayProperty =
            AvaloniaProperty.Register<MediaElement, bool>(nameof(AutoPlay), false);

        public bool AutoPlay
        {
            get => GetValue(AutoPlayProperty);
            set => SetValue(AutoPlayProperty, value);
        }

        public static readonly StyledProperty<int> VolumeProperty =
            AvaloniaProperty.Register<MediaElement, int>(nameof(Volume), 100);

        public int Volume
        {
            get => GetValue(VolumeProperty);
            set => SetValue(VolumeProperty, value);
        }

        public static readonly StyledProperty<bool> IsMutedProperty =
            AvaloniaProperty.Register<MediaElement, bool>(nameof(IsMuted), false);

        public bool IsMuted
        {
            get => GetValue(IsMutedProperty);
            set => SetValue(IsMutedProperty, value);
        }

        public static readonly StyledProperty<long> TimeProperty =
            AvaloniaProperty.Register<MediaElement, long>(nameof(Time), 0L);

        public long Time
        {
            get => GetValue(TimeProperty);
            set => SetValue(TimeProperty, value);
        }

        public static readonly StyledProperty<long> LengthProperty =
            AvaloniaProperty.Register<MediaElement, long>(nameof(Length), 0L);

        public long Length
        {
            get => GetValue(LengthProperty);
            private set => SetValue(LengthProperty, value);
        }

        public static readonly StyledProperty<float> RateProperty =
            AvaloniaProperty.Register<MediaElement, float>(nameof(Rate), 1.0f);

        public float Rate
        {
            get => GetValue(RateProperty);
            set => SetValue(RateProperty, value);
        }

        public static readonly StyledProperty<int> SpuProperty =
            AvaloniaProperty.Register<MediaElement, int>(nameof(Spu), -1);

        public int Spu
        {
            get => GetValue(SpuProperty);
            set => SetValue(SpuProperty, value);
        }

        public static readonly StyledProperty<Stretch> StretchProperty =
            AvaloniaProperty.Register<MediaElement, Stretch>(nameof(Stretch), Stretch.Fill);

        public Stretch Stretch
        {
            get => GetValue(StretchProperty);
            set => SetValue(StretchProperty, value);
        }

        public static readonly StyledProperty<string> AspectRatioProperty =
            AvaloniaProperty.Register<MediaElement, string>(nameof(AspectRatio));

        public string AspectRatio
        {
            get => GetValue(AspectRatioProperty);
            set => SetValue(AspectRatioProperty, value);
        }

        public static readonly StyledProperty<double> BufferingProgressProperty =
            AvaloniaProperty.Register<MediaElement, double>(nameof(BufferingProgress), 0.0);

        public double BufferingProgress
        {
            get => GetValue(BufferingProgressProperty);
            private set => SetValue(BufferingProgressProperty, value);
        }

        static MediaElement()
        {
            if (Design.IsDesignMode)
                return;

            try
            {
                Core.Initialize(null);

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    libVLC = new LibVLC("--directx-use-sysmem", "--network-caching=2000", "--no-plugins-cache");
                else
                    libVLC = new LibVLC("--network-caching=2000", "--no-plugins-cache");
            }
            catch (Exception ex)
            {
                Console.WriteLine("[MediaElement] LibVLC init failed: " + ex);
            }
        }

        public MediaElement()
        {
            if (libVLC is null)
            {
                Content = null;
                return;
            }

            seekDebounceTimer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(120)
            };
            seekDebounceTimer.Tick += (s, e) => FlushPendingSeek();

            mediaPlayer = new MediaPlayer(libVLC);
            videoView = new VideoView { MediaPlayer = mediaPlayer };
            Content = videoView;

            HookPropertyObservers();
            HookPlayerEvents(mediaPlayer);

            ApplyAllToPlayer();
        }

        protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
        {
            DetachVideoViewEarly();
            base.OnDetachedFromVisualTree(e);
        }

        private void HookPropertyObservers()
        {
            this.GetObservable(SourceProperty).Subscribe(uri => _ = OnSourceChangedAsync(uri));

            this.GetObservable(RateProperty).Subscribe(r =>
            {
                if (isDisposed)
                    return;

                Try(() => mediaPlayer?.SetRate(r));
            });

            this.GetObservable(SpuProperty).Subscribe(s =>
            {
                if (isDisposed)
                    return;

                Try(() => mediaPlayer?.SetSpu(s));
            });

            this.GetObservable(VolumeProperty).Subscribe(v =>
            {
                if (isDisposed || mediaPlayer is null)
                    return;

                var clamped = Math.Clamp(v, 0, 100);
                Try(() =>
                {
                    if (mediaPlayer.Volume != clamped)
                        mediaPlayer.Volume = clamped;
                });
            });

            this.GetObservable(IsMutedProperty).Subscribe(m =>
            {
                if (isDisposed || mediaPlayer is null || updatingMuteFromPlayer)
                    return;

                Try(() =>
                {
                    if (mediaPlayer.Mute != m)
                        mediaPlayer.Mute = m;
                });
            });

            this.GetObservable(TimeProperty).Subscribe(t =>
            {
                if (isDisposed || mediaPlayer is null || updatingTimeFromPlayer)
                    return;

                pendingSeekTime = Math.Max(0, t);
                hasPendingSeek = true;
                isScrubbing = true;

                seekDebounceTimer?.Stop();
                seekDebounceTimer?.Start();
            });

            this.GetObservable(BoundsProperty).Subscribe(_ => ScheduleApplyStretch());
        }

        private void HookPlayerEvents(MediaPlayer player)
        {
            pausedHandler = (s, e) => UI(() => Paused?.Invoke(this, EventArgs.Empty));

            stoppedHandler = (s, e) => UI(() =>
            {
                ApplyAudioToPlayer();
                isStopping = false;
                Stopped?.Invoke(this, EventArgs.Empty);
            });

            playingHandler = (s, e) => UI(() =>
            {
                isStopping = false;
                ApplyAudioToPlayer();
                Playing?.Invoke(this, EventArgs.Empty);
            });

            volumeChangedHandler = (s, e) => UI(() =>
            {
                if (isStopping)
                    return;

                VolumeChanged?.Invoke(this, EventArgs.Empty);
            });

            mutedHandler = (s, e) => UI(() =>
            {
                updatingMuteFromPlayer = true;
                SetCurrentValue(IsMutedProperty, true);
                updatingMuteFromPlayer = false;

                Muted?.Invoke(this, EventArgs.Empty);
            });

            unmutedHandler = (s, e) => UI(() =>
            {
                updatingMuteFromPlayer = true;
                SetCurrentValue(IsMutedProperty, false);
                updatingMuteFromPlayer = false;

                Unmuted?.Invoke(this, EventArgs.Empty);
            });

            timeChangedHandler = (s, e) => UI(() =>
            {
                if (mediaPlayer is null)
                    return;

                if (isScrubbing)
                {
                    TimeChanged?.Invoke(this, EventArgs.Empty);
                    return;
                }

                updatingTimeFromPlayer = true;
                SetCurrentValue(TimeProperty, mediaPlayer.Time);
                updatingTimeFromPlayer = false;

                TimeChanged?.Invoke(this, EventArgs.Empty);
            });

            lengthChangedHandler = (s, e) => UI(() =>
            {
                if (mediaPlayer is null)
                    return;

                Length = mediaPlayer.Length;
                LengthChanged?.Invoke(this, EventArgs.Empty);
            });

            bufferingHandler = (s, e) => UI(() =>
            {
                BufferingProgress = e.Cache / 100.0;
                Buffering?.Invoke(this, EventArgs.Empty);
            });

            endReachedHandler = (s, e) => UI(() =>
            {
                if (mediaPlayer is null)
                    return;

                Try(() =>
                {
                    isStopping = true;
                    mediaPlayer.Stop();
                    mediaPlayer.Time = 0;

                    updatingTimeFromPlayer = true;
                    SetCurrentValue(TimeProperty, 0L);
                    updatingTimeFromPlayer = false;

                    ApplyAudioToPlayer();
                });

                MediaEnded?.Invoke(this, EventArgs.Empty);
            });

            player.Paused += pausedHandler;
            player.Stopped += stoppedHandler;
            player.Playing += playingHandler;
            player.VolumeChanged += volumeChangedHandler;
            player.Muted += mutedHandler;
            player.Unmuted += unmutedHandler;
            player.TimeChanged += timeChangedHandler;
            player.LengthChanged += lengthChangedHandler;
            player.Buffering += bufferingHandler;
            player.EndReached += endReachedHandler;
        }

        private void UnhookPlayerEvents(MediaPlayer player)
        {
            if (pausedHandler is not null) player.Paused -= pausedHandler;
            if (stoppedHandler is not null) player.Stopped -= stoppedHandler;
            if (playingHandler is not null) player.Playing -= playingHandler;
            if (volumeChangedHandler is not null) player.VolumeChanged -= volumeChangedHandler;
            if (mutedHandler is not null) player.Muted -= mutedHandler;
            if (unmutedHandler is not null) player.Unmuted -= unmutedHandler;
            if (timeChangedHandler is not null) player.TimeChanged -= timeChangedHandler;
            if (lengthChangedHandler is not null) player.LengthChanged -= lengthChangedHandler;
            if (bufferingHandler is not null) player.Buffering -= bufferingHandler;
            if (endReachedHandler is not null) player.EndReached -= endReachedHandler;
        }

        private void FlushPendingSeek()
        {
            seekDebounceTimer?.Stop();

            if (isDisposed || mediaPlayer is null)
            {
                hasPendingSeek = false;
                isScrubbing = false;
                return;
            }

            if (!hasPendingSeek)
            {
                isScrubbing = false;
                return;
            }

            hasPendingSeek = false;

            Try(() => mediaPlayer.Time = pendingSeekTime);

            Dispatcher.UIThread.Post(() =>
            {
                if (!isDisposed)
                    isScrubbing = false;
            }, DispatcherPriority.Background);
        }

        public void Seek(TimeSpan timeSpan)
        {
            if (mediaPlayer is null || isDisposed)
                return;

            var ms = (long)Math.Max(0, timeSpan.TotalMilliseconds);

            pendingSeekTime = ms;
            hasPendingSeek = true;
            isScrubbing = true;

            seekDebounceTimer?.Stop();
            seekDebounceTimer?.Start();

            updatingTimeFromPlayer = true;
            SetCurrentValue(TimeProperty, ms);
            updatingTimeFromPlayer = false;
        }

        public void SeekToPercentage(double percentage)
        {
            if (mediaPlayer is null || isDisposed)
                return;

            var len = mediaPlayer.Length;

            if (percentage > 100 || (len <= 0 && percentage > 1))
            {
                var ms = (long)Math.Max(0, percentage);

                pendingSeekTime = ms;
                hasPendingSeek = true;
                isScrubbing = true;

                seekDebounceTimer?.Stop();
                seekDebounceTimer?.Start();

                updatingTimeFromPlayer = true;
                SetCurrentValue(TimeProperty, ms);
                updatingTimeFromPlayer = false;
                return;
            }

            var p = percentage;
            if (p > 1)
                p /= 100.0;

            p = Math.Clamp(p, 0, 1);

            Try(() => mediaPlayer.Position = (float)p);

            if (len > 0)
            {
                var ms = (long)(len * p);
                updatingTimeFromPlayer = true;
                SetCurrentValue(TimeProperty, ms);
                updatingTimeFromPlayer = false;
            }
        }

        public bool IsPlaying => mediaPlayer?.IsPlaying ?? false;

        public void Play() => mediaPlayer?.Play();

        public Task PlayAsync() => Dispatcher.UIThread.InvokeAsync(() => mediaPlayer?.Play()).GetTask();

        public bool CanPlay() => !(mediaPlayer?.IsPlaying ?? false);

        public void Stop()
        {
            if (mediaPlayer is null || isDisposed)
                return;

            isStopping = true;

            Try(() => mediaPlayer.Stop());

            UI(ApplyAudioToPlayer);
        }

        public bool CanStop() => mediaPlayer?.IsPlaying ?? false;

        public void Pause() => mediaPlayer?.Pause();

        public bool CanPause() => mediaPlayer?.IsPlaying ?? false;

        public void TogglePlayPause()
        {
            if (mediaPlayer?.IsPlaying == true)
                mediaPlayer.Pause();
            else
                mediaPlayer?.Play();
        }

        public void Mute() => IsMuted = true;

        public bool CanMute() => !IsMuted;

        public void Unmute() => IsMuted = false;

        public bool CanUnmute() => IsMuted;

        public void ToggleMute() => IsMuted = !IsMuted;

        public void ToggleCloseCaption()
        {
            if (Spu != -1)
                Spu = -1;
            else
                Spu = 0;
        }

        public bool IsSeekable => mediaPlayer?.IsSeekable ?? false;

        public void Download()
        {
            if (Source?.Scheme != "avares")
                Process.Start(new ProcessStartInfo { FileName = mediaPlayer?.Media?.Mrl, UseShellExecute = true });
        }

        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
        {
            base.OnAttachedToVisualTree(e);
            isVideoDetached = false;

            ApplyAllToPlayer();

            if (AutoPlay)
                mediaPlayer?.Play();
        }

        private async Task OnSourceChangedAsync(Uri uri)
        {
            if (isDisposed || libVLC is null || mediaPlayer is null)
                return;

            if (uri is null)
                return;

            var old = Interlocked.Exchange(ref sourceCts, new CancellationTokenSource());
            Try(() =>
            {
                old?.Cancel();
                old?.Dispose();
            });

            var token = sourceCts!.Token;

            try
            {
                UI(() =>
                {
                    Length = 0;

                    updatingTimeFromPlayer = true;
                    SetCurrentValue(TimeProperty, 0L);
                    updatingTimeFromPlayer = false;

                    pendingSeekTime = 0;
                    hasPendingSeek = false;
                    isScrubbing = false;
                    seekDebounceTimer?.Stop();
                });

                CleanupCurrentMedia();

                token.ThrowIfCancellationRequested();

                Media media;

                if (uri.Scheme == "avares")
                {
                    // 把资源变成“稳定可 seek + 有长度”的 MemoryStream
                    using var src = AssetLoader.Open(uri);
                    var ms = new MemoryStream();
                    src.CopyTo(ms);
                    ms.Position = 0;

                    currentStream = ms;
                    currentMediaInput = new StreamMediaInput(currentStream);

                    media = new Media(libVLC, currentMediaInput);
                }
                else
                {
                    media = new Media(libVLC, uri);
                }

                // 明确告诉 VLC 不要做各种缓存(否则 stream input 很容易落盘到 temp)
                ConfigureMediaOptions(media);

                if (token.IsCancellationRequested || isDisposed || mediaPlayer is null)
                {
                    Try(() => media.Dispose());
                    return;
                }

                mediaPlayer.Media = media;
                currentMedia = media;

                parsedChangedHandler = (s, e) =>
                {
                    UI(() =>
                    {
                        if (isDisposed)
                            return;

                        foreach (var track in media.Tracks)
                        {
                            if (track.TrackType == TrackType.Video)
                            {
                                var videoTrack = track.Data.Video;
                                VideoWidth = videoTrack.Width;
                                VideoHeight = videoTrack.Height;
                                ScheduleApplyStretch();
                                break;
                            }
                        }
                    });
                };

                media.ParsedChanged += parsedChangedHandler;

                // Parse 可选:为了拿 track 信息;取消会提前退出
                try
                {
                    await Task.Run(() =>
                    {
                        if (token.IsCancellationRequested)
                            return;

                        try { media.Parse(MediaParseOptions.ParseNetwork); }
                        catch
                        {
                            try { media.Parse(); } catch { }
                        }
                    }, token);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
                catch
                {
                }

                if (token.IsCancellationRequested || isDisposed || mediaPlayer is null)
                    return;

                ApplyAllToPlayer();

                if (AutoPlay && VisualRoot is not null && !isDisposed)
                    mediaPlayer.Play();
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception ex)
            {
                Console.WriteLine("[MediaElement] 警告:媒体播放失败:" + ex.Message, ex);
            }
        }

        // “禁止大量临时文件”的核心
        private static void ConfigureMediaOptions(Media media)
        {
            // 目标:不要为了 demux/seek 去做落盘缓存
            media.AddOption(":file-caching=0");
            media.AddOption(":network-caching=2000");
            media.AddOption(":live-caching=0");
            media.AddOption(":disc-caching=0");
            media.AddOption(":sout-mux-caching=0");

            media.AddOption(":no-sub-autodetect-file");
            media.AddOption(":no-video-title-show");
        }

        private void CleanupCurrentMedia()
        {
            if (currentMedia is not null && parsedChangedHandler is not null)
                Try(() => currentMedia.ParsedChanged -= parsedChangedHandler);

            parsedChangedHandler = null;

            Try(() => currentMedia?.Dispose());
            currentMedia = null;

            Try(() => mediaPlayer?.Media?.Dispose());
            if (mediaPlayer is not null)
                mediaPlayer.Media = null;

            Try(() => currentMediaInput?.Dispose());
            currentMediaInput = null;

            Try(() => currentStream?.Dispose());
            currentStream = null;
        }

        private void ApplyAudioToPlayer()
        {
            if (mediaPlayer is null || isDisposed)
                return;

            Try(() =>
            {
                mediaPlayer.Volume = Math.Clamp(Volume, 0, 100);
                mediaPlayer.Mute = IsMuted;
            });
        }

        private void ApplyAllToPlayer()
        {
            if (mediaPlayer is null || isDisposed)
                return;

            Try(() =>
            {
                ApplyAudioToPlayer();
                mediaPlayer.SetRate(Rate);
                mediaPlayer.SetSpu(Spu);

                if (!string.IsNullOrWhiteSpace(AspectRatio))
                    mediaPlayer.AspectRatio = AspectRatio;

                if (Time > 0)
                    mediaPlayer.Time = Math.Max(0, Time);
            });
        }

        private void ScheduleApplyStretch()
        {
            if (stretchPending)
                return;

            stretchPending = true;

            Dispatcher.UIThread.Post(() =>
            {
                stretchPending = false;

                if (isDisposed || videoView is null || mediaPlayer is null)
                    return;

                if (VideoWidth <= 0 || VideoHeight <= 0)
                {
                    videoView.Width = double.NaN;
                    videoView.Height = double.NaN;
                    videoView.HorizontalAlignment = HorizontalAlignment.Stretch;
                    videoView.VerticalAlignment = VerticalAlignment.Stretch;
                    return;
                }

                var availableWidth = Bounds.Width;
                var availableHeight = Bounds.Height;
                if (availableWidth <= 0 || availableHeight <= 0)
                    return;

                var aspectVideo = (double)VideoWidth / VideoHeight;
                var aspectContainer = availableWidth / availableHeight;

                switch (Stretch)
                {
                    case Stretch.None:
                        AspectRatio = $"{VideoWidth}:{VideoHeight}";
                        videoView.Width = VideoWidth;
                        videoView.Height = VideoHeight;
                        videoView.HorizontalAlignment = HorizontalAlignment.Left;
                        videoView.VerticalAlignment = VerticalAlignment.Top;
                        break;

                    case Stretch.Fill:
                        AspectRatio = $"{availableWidth}:{availableHeight}";
                        videoView.Width = availableWidth;
                        videoView.Height = availableHeight;
                        videoView.HorizontalAlignment = HorizontalAlignment.Stretch;
                        videoView.VerticalAlignment = VerticalAlignment.Stretch;
                        break;

                    case Stretch.Uniform:
                        AspectRatio = $"{VideoWidth}:{VideoHeight}";
                        if (aspectContainer > aspectVideo)
                        {
                            videoView.Height = availableHeight;
                            videoView.Width = availableHeight * aspectVideo;
                        }
                        else
                        {
                            videoView.Width = availableWidth;
                            videoView.Height = availableWidth / aspectVideo;
                        }
                        videoView.HorizontalAlignment = HorizontalAlignment.Center;
                        videoView.VerticalAlignment = VerticalAlignment.Center;
                        break;

                    case Stretch.UniformToFill:
                        if (aspectContainer > aspectVideo)
                        {
                            var height = availableWidth / aspectVideo;
                            AspectRatio = $"{availableWidth}:{height}";
                            videoView.Width = availableWidth;
                            videoView.Height = height;
                        }
                        else
                        {
                            var width = availableHeight * aspectVideo;
                            AspectRatio = $"{width}:{availableHeight}";
                            videoView.Height = availableHeight;
                            videoView.Width = width;
                        }
                        videoView.HorizontalAlignment = HorizontalAlignment.Center;
                        videoView.VerticalAlignment = VerticalAlignment.Center;
                        break;
                }

                Try(() =>
                {
                    if (!string.IsNullOrWhiteSpace(AspectRatio))
                        mediaPlayer.AspectRatio = AspectRatio;
                });
            });
        }

        private void DetachVideoViewEarly()
        {
            if (isVideoDetached)
                return;

            isVideoDetached = true;

            if (videoView is null)
                return;

            if (Dispatcher.UIThread.CheckAccess())
            {
                videoView.MediaPlayer = null;
                return;
            }

            Dispatcher.UIThread.Post(() =>
            {
                if (isDisposed || videoView is null)
                    return;

                videoView.MediaPlayer = null;
            }, DispatcherPriority.Send);
        }

        private void UI(Action action)
        {
            if (isDisposed)
                return;

            if (Dispatcher.UIThread.CheckAccess())
            {
                action();
                return;
            }

            Dispatcher.UIThread.Post(() =>
            {
                if (!isDisposed)
                    action();
            });
        }

        private static void Try(Action action)
        {
            try { action(); } catch { }
        }

        public void Dispose()
        {
            if (isDisposed)
                return;

            isDisposed = true;

            Try(() =>
            {
                sourceCts?.Cancel();
                sourceCts?.Dispose();
            });
            sourceCts = null;

            Try(() => seekDebounceTimer?.Stop());

            DetachVideoViewEarly();

            if (mediaPlayer is not null)
                Try(() => UnhookPlayerEvents(mediaPlayer));

            CleanupCurrentMedia();

            Try(() => mediaPlayer?.Stop());
            Try(() => mediaPlayer?.Dispose());
            mediaPlayer = null;

            videoView = null;
        }
    }
}

AssemblyResources.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         Height="300" Width="300"
        x:Class="AvaloniaUI.AssemblyResources"
        Title="AssemblyResources">
    <!--https://github.com/videolan/libvlcsharp/tree/3.x-->

    <Grid RowDefinitions="*,auto">
        <MediaElement Name="Sound" Source="avares://AvaloniaUI/Resources/Sounds/金刚经.mp4"/>
        <StackPanel Grid.Row="1">
            <Button Name="Play" Click="cmdPlay_Click" Content="Play">
                <Button.Background>
                    <ImageBrush Source="avares://AvaloniaUI/Resources/Images/Blue hills.jpg" Stretch="Fill"/>
                </Button.Background>
            </Button>
        </StackPanel>
    </Grid>
</Window>

AssemblyResources.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Shares.Avalonia;
using System;

namespace AvaloniaUI;

public partial class AssemblyResources : Window
{
    public AssemblyResources()
    {
        InitializeComponent();
    }


    private void cmdPlay_Click(object? sender, RoutedEventArgs e)
    {
        var uri = new Uri("avares://AvaloniaUI/Resources/Images/Winter.jpg");
        using var stream = AssetLoader.Open(uri);
        Play.Background = new ImageBrush() { Source = new Bitmap(stream), Stretch = Stretch.Fill };
        Sound.Play();
    }
}

运行效果

image

 

posted on 2025-07-30 11:03  dalgleish  阅读(115)  评论(0)    收藏  举报