什么是 MediaElement?
借助 MediaElement,您可以获得强大的控件,允许您在 .NET MAUI 应用程序内播放多媒体。
您可能已经从 Xamarin 社区工具包中了解 MediaElement,它是由社区成员 Peter Foot 的出色工作添加的。虽然这个版本已经相当不错了,但它还有改进的空间,尤其是在 Android 上。
这就是为什么当将 MediaElement 移植到 .NET MAUI 时,我们从头开始重建了一切。这样我们就可以确保保留所有已经好的部分,同时改进那些需要一些爱的东西。
底层引擎
对于 Android,我们选择用作ExoPlayer平台对应项,替换我们用于 Xamarin 的 Android MediaPlayer。通过这种方式,我们可以自动获得许多开箱即用的额外功能,例如播放 HTTP Live Streaming (HLS) 视频、美观的平台传输控件以及许多其他功能。
在 iOS 和 macOS 上,我们使用该平台的方式AVPlayer与使用 Xamarin 的 MediaElement 的方式相同。 Tizen 也没有改变,使用Tizen.Multimedia.Player.
现在 .NET MAUI 构建在 WinUI 而不是 UWP 之上,我们MediaPlayerElement在这里使用 WinUI 的全新功能。虽然这个控件对于 WinUI 来说还很新,但它已经非常完整而且潜力巨大。
对不同媒体格式的支持因平台(以及可能安装的编解码器)而异,但通过使用平台本机媒体播放器,我们可以利用每个操作系统的所有功能和相关的优化性能。
入门
MediaElement 入门很简单。首先您要安装CommunityToolkit.Maui.MediaElementNuGet 包。这是一个独立于主要社区工具包包的包。
安装完成后,进入您的MauiProgram.cs并将以下初始化行添加到MauiAppBuilder:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
// Initialize the .NET MAUI Community Toolkit MediaElement by adding the below line of code
.UseMauiCommunityToolkitMediaElement()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
return builder.Build();
}
}
现在准备好开始在应用程序中使用 MediaElement!下面是 XAML 中的一个简单示例。
这个代码直接是现在MainPage.xaml中,只有一个页面
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="VideoPlayer.MainPage">
<!--RowDefinitions:设置每行高度
ColumnDefinitions:设置每列宽度*表示填满-->
<Grid
Padding="5"
RowDefinitions="240,30,50,*,50"
ColumnDefinitions="*"
>
<!--Row和Column都是从0开始-->
<toolkit:MediaElement
Grid.Row="0"
Grid.Column="0"
x:Name="mediaElement"
ShouldAutoPlay="True"
ShouldShowPlaybackControls="True"
Source=""
HeightRequest="240"
Unloaded="mediaElement_Unloaded"
StateChanged="mediaElement_StateChanged"
MediaEnded="mediaElement_MediaEnded"
MediaOpened="mediaElement_MediaOpened"
/>
<Label Grid.Row="1" Grid.Column="0" x:Name="labIndex" FontSize="14" TextColor="DarkCyan" ></Label>
<HorizontalStackLayout
Grid.Row="2"
Grid.Column="0"
Padding="5"
Spacing="20"
>
<Label x:Name="labTotal" Padding="5" FontSize="18" ></Label>
<Button x:Name="btnLast" Padding="10" Clicked="btnLast_Clicked" Text="上一首"></Button>
<Button x:Name="btnPlay" Padding="10" Clicked="btnPlay_Clicked" Text=" 播放 "></Button>
<Button x:Name="btnPause" Padding="10" Clicked="btnPause_Clicked" Text=" 暂停 "></Button>
<Button x:Name="btnNext" Padding="10" Clicked="btnNext_Clicked" Text="下一首"></Button>
</HorizontalStackLayout>
<ListView
Grid.Row="3"
Grid.Column="0"
SeparatorColor="Silver"
BackgroundColor="Transparent"
x:Name="videoList"
ItemSelected="videoList_ItemSelected"
ItemTapped="videoList_ItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}"></TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button
Grid.Row="4"
Grid.Column="0"
x:Name="btnPick" Text="Pick" Clicked="btnPick_Clicked"/>
</Grid>
</ContentPage>
MainPage.xmal.cs:
using System.Collections.ObjectModel;
using VideoPlayer.Models;
namespace VideoPlayer
{
public partial class MainPage : ContentPage
{
/// <summary>
/// 目前mediaElement不支持列表所以需要自己设置列表
/// </summary>
private List<Video> videoSources = new List<Video> { };
private int currentVideoIndex;
public MainPage()
{
InitializeComponent();
btnPlay.IsVisible = false;
labTotal.Text = "Total:0";
}
/// <summary>
/// 打开文件选择器选择文件
/// </summary>
private async void Test()
{
//多选方法
var filePickerResults = await FilePicker.PickMultipleAsync(new PickOptions
{
FileTypes = FilePickerFileType.Videos,
PickerTitle = "Select a file"
});
if (filePickerResults != null)
{
videoSources=new List<Video>();
foreach (var filePickerResult in filePickerResults)
{
videoSources.Add(new Video(Path.GetFileNameWithoutExtension(filePickerResult.FileName), filePickerResult.FullPath));
}
mediaElement.Source = videoSources[currentVideoIndex].Path;
}
videoList.ItemsSource = new ObservableCollection<Video>(videoSources);
labTotal.Text = "Total:" +videoSources.Count.ToString();
}
private void btnPick_Clicked(object sender, EventArgs e)
{
Test();
}
private void mediaElement_Unloaded(object sender, EventArgs e)
{
// 释放资源
mediaElement.Handler?.DisconnectHandler();
}
/// <summary>
/// 切换逻辑
/// </summary>
private void Next()
{
if (currentVideoIndex >= videoSources.Count)
currentVideoIndex = 0;
mediaElement.Source = videoSources[currentVideoIndex].Path;
}
/// <summary>
/// 播放状态事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void mediaElement_StateChanged(object sender, CommunityToolkit.Maui.Core.Primitives.MediaStateChangedEventArgs e)
{
if(e.NewState == CommunityToolkit.Maui.Core.Primitives.MediaElementState.Paused)
{
btnPlay.IsVisible = true;
btnPause.IsVisible = false;
}else if(e.NewState == CommunityToolkit.Maui.Core.Primitives.MediaElementState.Playing)
{
btnPlay.IsVisible = false;
btnPause.IsVisible = true;
}
}
/// <summary>
/// 播放结束事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void mediaElement_MediaEnded(object sender, EventArgs e)
{
currentVideoIndex++;
Next();
}
/// <summary>
/// 列表Item选择事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void videoList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if(videoList.SelectedItem != null)
{
currentVideoIndex = e.SelectedItemIndex;
Next();
}
}
/// <summary>
/// 选择事件触发后触发,重置SelectedItem
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void videoList_ItemTapped(object sender, ItemTappedEventArgs e)
{
videoList.SelectedItem = null;
}
/// <summary>
/// 上一首
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLast_Clicked(object sender, EventArgs e)
{
currentVideoIndex--;
Next();
}
/// <summary>
/// 播放按钮事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPlay_Clicked(object sender, EventArgs e)
{
btnPlay.IsVisible = false;
btnPause.IsVisible = true;
mediaElement.Play();
}
/// <summary>
/// medidaElement播放事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void mediaElement_MediaOpened(object sender, EventArgs e)
{
labIndex.Text = "当前播放:"+ videoSources[currentVideoIndex].Name;
//btnPlay.IsVisible = false;
}
/// <summary>
/// 暂停
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPause_Clicked(object sender, EventArgs e)
{
btnPlay.IsVisible = true;
btnPause.IsVisible = false;
mediaElement.Pause();
}
/// <summary>
/// 下一首
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnNext_Clicked(object sender, EventArgs e)
{
currentVideoIndex++;
Next();
}
}
}
最终效果
总体来讲MAUI还有很长的路要走
仓库地址:gitee