C# Avalonia没有内置的MediaElement,我们用LibVLC实现一个就行了。Avalonia官方实现的,要收费。配置文章里更新了。https://www.cnblogs.com/dalgleish/p/18967204
MediaElement类,实现了播放器大多数功能。
public class MediaElement : UserControl, IDisposable
{
private static LibVLC libVLC;
private readonly VideoView videoView;
private readonly MediaPlayer mediaPlayer;
private uint videoWidth;
private uint videoHeight;
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<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<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()
{
Core.Initialize();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
libVLC = new LibVLC("--directx-use-sysmem", "--network-caching=2000");
else
libVLC = new LibVLC("--network-caching=2000");
/*
#if DEBUG
libVLC.Log += (sender, e) =>
{
Console.WriteLine($"[LibVLC 日志][{e.Level}] {e.Module}: {e.Message}");
};
#endif
*/
}
public MediaElement()
{
mediaPlayer = new MediaPlayer(libVLC);
videoView = new VideoView()
{
MediaPlayer = mediaPlayer
};
this.Content = videoView;
this.GetObservable(SourceProperty).Subscribe(s => OnSourceChanged(s));
this.GetObservable(RateProperty).Subscribe(r => mediaPlayer.SetRate(r));
this.GetObservable(SpuProperty).Subscribe(s => mediaPlayer.SetSpu(s));
this.GetObservable(BoundsProperty).Subscribe(_ => ScheduleApplyStretch());
OnMediaPlayerChanged(mediaPlayer);
}
public void Seek(TimeSpan timeSpan)
{
if (IsSeekable)
{
Time = (long)(timeSpan.TotalMilliseconds * 1000); // 转换为微秒
}
}
// 按百分比跳转
public void SeekToPercentage(double percentage)
{
if (IsSeekable && mediaPlayer?.Length > 0)
{
Time = (long)(mediaPlayer.Length * percentage);
}
}
public bool IsPlaying => mediaPlayer.IsPlaying;
public void Play() => mediaPlayer.Play();
public bool CanPlay() => !mediaPlayer.IsPlaying;
public void Stop() => mediaPlayer.Stop();
public bool CanStop() => mediaPlayer.IsPlaying;
public void Pause() => mediaPlayer.Pause();
public bool CanPause() => mediaPlayer.IsPlaying;
public void TogglePlayPause()
{
if (mediaPlayer.IsPlaying)
{
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;
public long Length => mediaPlayer.Length;
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);
if (AutoPlay)
{
mediaPlayer.Play();
}
}
private void OnSourceChanged(Uri uri)
{
if (uri == null)
return;
try
{
Media? media;
if (uri.Scheme == "avares")
{
var stream = new StreamMediaInput(AssetLoader.Open(uri));
media = new Media(libVLC, stream);
}
else
media = new Media(libVLC, uri);
mediaPlayer.Media = media;
media.ParsedChanged += (s, e) =>
{
foreach (var track in media.Tracks)
{
if (track.TrackType == TrackType.Video)
{
VideoTrack videoTrack = track.Data.Video;
videoWidth = videoTrack.Width;
videoHeight = videoTrack.Height;
ScheduleApplyStretch();
break;
}
}
};
}
catch (Exception ex)
{
Console.WriteLine("[MediaElement] 警告:媒体播放失败:" + ex.Message, ex);
}
}
private void OnMediaPlayerChanged(MediaPlayer newPlayer)
{
if (newPlayer == null)
{
Console.WriteLine("[MediaElement] 警告:newPlayer不能为空.");
return;
}
newPlayer.Paused += (s, e) => Paused?.Invoke(s, e);
newPlayer.Stopped += (s, e) => Stopped?.Invoke(s, e);
newPlayer.Playing += (s, e) => Playing?.Invoke(s, e);
newPlayer.VolumeChanged += (s, e) => VolumeChanged?.Invoke(s, e);
newPlayer.TimeChanged += (s, e) => TimeChanged?.Invoke(s, e);
newPlayer.LengthChanged += (s, e) => LengthChanged?.Invoke(s, e);
newPlayer.Muted += (s, e) => Muted?.Invoke(s, e);
newPlayer.Unmuted += (s, e) => Unmuted?.Invoke(s, e);
newPlayer.Buffering += (s, e) =>
{
Dispatcher.UIThread.Post(() =>
{
BufferingProgress = e.Cache / 100.0;
Buffering?.Invoke(s, e);
});
};
newPlayer.EndReached += (s, e) =>
{
//停止并回到 0
Dispatcher.UIThread.Post(() =>
{
newPlayer.Stop();
newPlayer.Time = 0;
MediaEnded?.Invoke(this, EventArgs.Empty);
});
};
this[!!MediaElement.VolumeProperty] = new Binding() { Source = newPlayer, Path = nameof(Volume), Mode = BindingMode.TwoWay };
this[!!MediaElement.IsMutedProperty] = new Binding() { Source = newPlayer, Path = nameof(Mute), Mode = BindingMode.TwoWay };
this[!!MediaElement.TimeProperty] = new Binding() { Source = newPlayer, Path = nameof(Time), Mode = BindingMode.TwoWay };
this[!!MediaElement.AspectRatioProperty] = new Binding() { Source = newPlayer, Path = nameof(AspectRatio), Mode = BindingMode.TwoWay };
}
private void ScheduleApplyStretch()
{
Dispatcher.UIThread.Post(() =>
{
if (videoWidth <= 0 || videoHeight <= 0)
{
videoView.Width = double.NaN;
videoView.Height = double.NaN;
videoView.HorizontalAlignment = HorizontalAlignment.Stretch;
videoView.VerticalAlignment = VerticalAlignment.Stretch;
return;
}
var availableWidth = this.Bounds.Width;
var availableHeight = this.Bounds.Height;
if (availableWidth <= 0 || availableHeight <= 0)
return;
double aspectVideo = (double)videoWidth / videoHeight;
double 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)
{
AspectRatio = $"{availableWidth}:{(int)availableWidth / aspectVideo}";
videoView.Width = availableWidth;
videoView.Height = availableWidth / aspectVideo;
}
else
{
AspectRatio = $"{(int)availableHeight * aspectVideo}:{availableHeight}";
videoView.Height = availableHeight;
videoView.Width = availableHeight * aspectVideo;
}
videoView.HorizontalAlignment = HorizontalAlignment.Center;
videoView.VerticalAlignment = VerticalAlignment.Center;
break;
}
});
}
public void Dispose()
{
mediaPlayer?.Media?.Dispose();
mediaPlayer?.Dispose();
}
}
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(); } }
运行效果

浙公网安备 33010602011771号