媒体播放器

媒体播放涉及通过内联(嵌入在页面中或使用一组其他控件)或专用全屏体验来观看和收听视频和音频。

用户需要一个基本控制集(如播放/暂停、快退、快进),你可以根据需要进行修改(包括媒体播放器的按钮、控件条的背景以及控件排列或布局)。

媒体播放器元素的屏幕截图,其中传输控件播放了一个虫子的视频

这是正确的控件吗?

当你想要在应用中播放音频或视频时,请使用媒体播放器。 若要显示图像集合,请使用 翻转视图

建议

媒体播放器支持浅色和深色主题,但深色主题可为大多数娱乐方案提供更好的体验。 深色背景可提供更好的对比度(特别是在低光照条件下),并限制控件栏以免干扰观看体验。

在播放视频内容时,通过优先使用全屏模式而不是内联模式来鼓励专注的观看体验。 全屏观看体验效果最佳,内联模式中选项受到限制。

如果你有屏幕空间,请使用双行布局。 它为控件提供了比精简的单行布局更多的空间,并且可以更轻松地使用各种输入进行导航。

默认控件已针对媒体播放进行了优化,但你可以向媒体播放器添加所需的自定义选项,以便为应用提供最佳体验。 若要了解有关添加自定义控件的详细信息,请访问创建自定义传输控件

UWP 和 WinUI 2

重要

本文中的信息和示例针对使用 Windows 应用 SDKWinUI 3 的应用进行优化,但通常适用于使用 WinUI 2 的 UWP 应用。 有关特定于平台的信息和示例,请参阅 UWP API 参考。

本部分包含你在 UWP 或 WinUI 2 应用中使用该控件所需的信息。

此控件的 API 存在于 Windows.UI.Xaml.Controls 命名空间中。

建议使用最新的 WinUI 2 来获取所有控件的最新样式和模板。 WinUI 2.2 或更高版本包含此控件的新模板,该模板使用圆角。 有关详细信息,请参阅圆角半径

如果要针对 10 英尺体验进行设计,请使用双行布局。 它为控件提供了比紧凑的单行布局更多的空间,并且使用 10 英尺的游戏板导航更容易。 有关针对 10 英尺体验优化应用程序的详细信息,请参阅针对 Xbox 和电视进行设计 一文。

MediaPlayerElement 仅在 Windows 10 版本 1607 及更高版本中可用。 如果要针对早期版本的 Windows 10 开发应用,你需要改用 MediaElement 控件。 此处 MediaElement 提出的所有建议也适用于 。

创建媒体播放器

WinUI 3 库应用包括大多数 WinUI 3 控件、特性和功能的交互式示例。 通过 Microsoft Store 获取应用,或在 GitHub 上获取源代码

通过使用 XAML 创建 MediaPlayerElement 对象来将媒体添加到你的应用,并将 Source 设置为指向某个音频或视频文件的 MediaSource

该 XAML 将创建一个 MediaPlayerElement,并将其 Source 属性设置为应用的某个本地视频文件的 URI。 页面 MediaPlayerElement 加载时开始播放。 若要禁止媒体立即启动,可以将 “自动播放 ”属性设置为 false

<MediaPlayerElement x:Name="mediaPlayerElement"
                    Source="ms-appx:///Videos/video1.mp4"
                    Width="400" AutoPlay="True"/>

此 XAML 创建一个 MediaPlayerElement ,其中启用了内置传输控件,并且 AutoPlay 属性设置为 false.

<MediaPlayerElement x:Name="mediaPlayerElement"
                    Source="ms-appx:///Videos/video1.mp4"
                    Width="400"
                    AutoPlay="False"
                    AreTransportControlsEnabled="True"/>

重要

将 设置为 MediaPlayerElement.Source 相对 URI (ms-appx/ms-resource) 仅适用于使用 Windows 应用程序打包项目打包的应用。 如果你的应用不使用 Windows 应用程序打包项目,建议的解决方法是将相对 ms-appx:/// URI 转换为完全解析的 file:/// URI。 另请参阅本文后面的 设置媒体源打开本地媒体文件 部分。

媒体传输控件

MediaPlayerElement 具有处理播放、停止、暂停、音量、静音、搜寻/进度、隐藏式字幕和音轨选择的内置传输控件。 若要启用这些控件,请将 AreTransportControlsEnabled 设置为 true。 若要禁用它们,请将 设置为 AreTransportControlsEnabledfalse。 传输控件由 MediaTransportControls 类表示。 你可以按原样使用传输控件,或通过各种方法自定义它们。 有关详细信息,请参阅 MediaTransportControls 类引用和创建自定义传输控件

传输控件支持单行和双行布局。 此处的第一个示例是单行布局,播放/暂停按钮位于媒体时间线左侧。 此布局最好专用于媒体播放和紧凑屏幕。

单行 MTC 控件示例

对于大多数使用方案,尤其是在较大的屏幕上,建议使用双行控件布局(下图)。 此布局为控件提供更多的空间,并且使用户可以更轻松地操作时间线。

双行 MTC 控件示例

系统媒体传输控件

MediaPlayerElement 自动与系统媒体传输控件集成。 系统媒体传输控件是在按下硬件媒体键时弹出的控件,例如键盘上的媒体按钮。 有关详细信息,请参阅 SystemMediaTransportControls

设置媒体源

若要播放网络上的文件或嵌入在应用中的文件,请将 Source 属性设置为带有该文件路径的 MediaSource

提示

若要从 Internet 打开文件,需要在应用的清单 中声明 Internet (客户端) 功能, (Package.appxmanifest) 。 有关声明功能的详细信息,请参阅应用功能声明

此代码尝试将使用 XAML 定义的 MediaPlayerElementSource 属性设置为输入到 TextBox 中的文件路径。

<TextBox x:Name="txtFilePath" Width="400"
         FontSize="20"
         KeyUp="TxtFilePath_KeyUp"
         Header="File path"
         PlaceholderText="Enter file path"/>
private void TxtFilePath_KeyUp(object sender, KeyRoutedEventArgs e)
{
    if (e.Key == Windows.System.VirtualKey.Enter)
    {
        TextBox tbPath = sender as TextBox;

        if (tbPath != null)
        {
            LoadMediaFromString(tbPath.Text);
        }
    }
}

private void LoadMediaFromString(string path)
{
    try
    {
        Uri pathUri = new Uri(path);
        mediaPlayerElement.Source = MediaSource.CreateFromUri(pathUri);
    }
    catch (Exception ex)
    {
        if (ex is FormatException)
        {
            // handle exception.
            // For example: Log error or notify user problem with file
        }
    }
}

若要将媒体源设置为嵌入在应用中的媒体文件,请初始化路径前缀为 的 ms-appx:///Uri,创建具有 Uri 的 MediaSource,然后将 Source 设置为 Uri。 例如,对于视频子文件夹中名为 video1.mp4 的文件,路径如下所示:ms-appx:///Videos/video1.mp4

重要

将 设置为 MediaPlayerElement.Source 相对 URI (ms-appx/ms-resource) 仅适用于使用 Windows 应用程序打包项目打包的应用。

此代码将先前在 XAML 中定义的 MediaPlayerElementSource 属性设置为 ms-appx:///Videos/video1.mp4

private void LoadEmbeddedAppFile()
{
    try
    {
        Uri pathUri = new Uri("ms-appx:///Videos/video1.mp4");
        mediaPlayerElement.Source = MediaSource.CreateFromUri(pathUri);
    }
    catch (Exception ex)
    {
        if (ex is FormatException)
        {
            // handle exception.
            // For example: Log error or notify user problem with file
        }
    }
}

打开本地媒体文件

若要打开本地系统上或 OneDrive 中的文件,你可以使用 FileOpenPicker 获取文件并使用 Source 设置媒体源,或者可以以编程方式访问用户媒体文件夹。

如果你的应用需要在不进行用户交互的情况下访问“音乐” 或“视频” 文件夹(例如,如果要枚举用户集锦中的所有音乐或视频文件并将它们显示在你的应用中),则你需要声明“音乐库” 和“视频库” 功能。 有关详细信息,请参阅音乐、图片和视频库中的文件和文件夹

FileOpenPicker 不需要特殊功能即可访问本地文件系统上的文件(例如用户的音乐视频文件夹),因为用户对所访问的文件具有完全控制权。 从安全性和隐私性角度来看,最好尽量减少你的应用使用的功能数。

使用 FileOpenPicker 打开本地媒体

  1. 调用 FileOpenPicker 以使用户选取媒体文件。

    使用 FileOpenPicker 类选择媒体文件。 设置 FileTypeFilter 以指定 显示的文件类型 FileOpenPicker 。 调用 PickSingleFileAsync 来启动文件选取器并获取文件。

  2. 使用 SetSource 将所选媒体文件设置为 MediaPlayerElement.Source

    若要使用从 FileOpenPicker 返回的 StorageFile,需要在 MediaSource 上调用 CreateFromStorageFile 方法,并将其设置为 MediaPlayerElement。 然后对 MediaPlayerElement.MediaPlayer 调用 Play 以启动媒体。

此示例演示如何使用 FileOpenPicker 选择文件并将该文件设置为 MediaPlayerElementSource

<MediaPlayerElement x:Name="mediaPlayerElement"/>
...
<Button Content="Choose file" Click="Button_Click"/>
private async void Button_Click(object sender, RoutedEventArgs e)
{
    await SetLocalMedia();
}

async private System.Threading.Tasks.Task SetLocalMedia()
{
    var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
    WinRT.Interop.InitializeWithWindow.Initialize(openPicker, WinRT.Interop.WindowNative.GetWindowHandle(this));

    openPicker.FileTypeFilter.Add(".wmv");
    openPicker.FileTypeFilter.Add(".mp4");
    openPicker.FileTypeFilter.Add(".wma");
    openPicker.FileTypeFilter.Add(".mp3");

    var file = await openPicker.PickSingleFileAsync();

    // mediaPlayerElement is a MediaPlayerElement control defined in XAML
    if (file != null)
    {
        mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(file);

        mediaPlayerElement.MediaPlayer.Play();
    }
}

设置海报源

你可以使用 PosterSource 属性在媒体加载前为 MediaPlayerElement 提供可视表示形式。 是 PosterSource 代替媒体显示的图像,如屏幕截图、电影海报或专辑图片。 PosterSource在以下情况下会显示 :

  • 在没有设置有效的源时。 例如, 未设置 SourceSource 将 设置为 null,或源无效 () 发生 MediaFailed 事件时的情况。
  • 加载媒体时。 例如,设置了有效源,但尚未发生 MediaOpened 事件。
  • 当媒体流式传输到其他设备时。
  • 当媒体仅限音频时。

以下是一个 MediaPlayerElement,其 Source 设置为唱片集,其 PosterSource 设置为唱片封面的图像。

<MediaPlayerElement Source="ms-appx:///Media/Track1.mp4" PosterSource="ms-appx:///Media/AlbumCover.png"/>

使设备的屏幕保持活动状态

通常,设备在用户离开时会降低屏幕亮度(并最终关闭屏幕)以延长电池使用时间,但视频应用需要保持屏幕打开状态以便用户可以观看视频。 若要阻止屏幕在不再检测到用户操作时(例如在应用播放视频时)被停用,可以调用 DisplayRequest.RequestActiveDisplayRequest 类允许你指示 Windows 保持屏幕打开状态,以便用户可以观看视频。

若要节省电源并延长电池使用时间,应调用 DisplayRequest.RequestRelease 以在不再需要显示请求时释放显示请求。 当你的应用移离屏幕时,Windows 会自动停用应用的活动显示请求,当你的应用回到前台时,会再次激活显示请求。

在以下情况下应该释放显示请求:

  • 例如,由于用户操作、缓冲或有限带宽引起的调整需要暂停视频播放。
  • 播放停止。 例如,视频播放完毕或完成演示文稿。
  • 出现播放错误。 例如,存在网络连接问题或损坏的文件。

使屏幕保持活动状态

  1. 创建一个全局 DisplayRequest 变量。 将其初始化为 null

    private DisplayRequest appDisplayRequest = null;
    
  2. 调用 RequestActive 来通知 Windows 此应用需要屏幕保持打开状态。

  3. 当视频播放停止、暂停或由于播放错误而中断时,调用 RequestRelease 来释放显示请求。 当你的应用不再包含任何活动的显示请求时,Windows 将在设备未使用时通过降低屏幕亮度(并最终关闭屏幕)来延长电池使用时间。

每个 MediaPlayerElement.MediaPlayer 都有 MediaPlaybackSession 类型的 PlaybackSession,用于控制媒体播放的各个方面,如 PlaybackRatePlaybackStatePosition。 此处,你在 MediaPlayer.PlaybackSession 上使用 PlaybackStateChanged 事件检测应释放显示请求的情况。 然后,使用 NaturalVideoHeight 属性确定某个音频或视频文件是否正在播放,并且仅在视频正在播放时使屏幕保持活动状态。

<MediaPlayerElement x:Name="mediaPlayerElement" Source="ms-appx:///Videos/video1.mp4"/>
public sealed partial class MainWindow : Window
{
    public DisplayRequest appDisplayRequest = null;
    // using Microsoft.UI.Dispatching;
    private DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();

    public MainWindow()
    {
        this.InitializeComponent();
        mediaPlayerElement.MediaPlayer.PlaybackSession.PlaybackStateChanged += 
            PlaybackSession_PlaybackStateChanged;
    }

    private void PlaybackSession_PlaybackStateChanged(MediaPlaybackSession sender, object args)
    {
        MediaPlaybackSession playbackSession = sender as MediaPlaybackSession;
        if (playbackSession != null && playbackSession.NaturalVideoHeight != 0)
        {
            if (playbackSession.PlaybackState == MediaPlaybackState.Playing)
            {
                if (appDisplayRequest is null)
                {
                    dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, () =>
                    {
                        appDisplayRequest = new DisplayRequest();
                        appDisplayRequest.RequestActive();
                    });
                }
            }
            else // PlaybackState is Buffering, None, Opening, or Paused.
            {
                if (appDisplayRequest is not null)
                {
                    appDisplayRequest.RequestRelease();
                    appDisplayRequest = null;
                }
            }
        }
    }
}

以编程方式控制媒体播放器

MediaPlayerElement 提供大量属性、方法和事件用于通过 MediaPlayerElement.MediaPlayer 属性控制音频和视频播放。 有关属性、方法和事件的完整列表,请参阅 MediaPlayer 参考页。

高级媒体播放方案

对于更复杂的媒体播放方案(如播放播放列表、在音频语言之间切换或创建自定义元数据曲目),请将 MediaPlayerElement.Source 设置为 MediaPlaybackItemMediaPlaybackList。 有关如何启用各种高级媒体功能的详细信息,请参阅媒体播放页。

调整视频大小和拉伸视频

使用 Stretch 属性更改视频内容和/或 PosterSource 填充它所在容器的方式。 这将根据 Stretch 值调整视频大小和拉伸视频。 这些 Stretch 状态类似于许多电视机上的图片大小设置。 你可以将它挂起到一个按钮并允许用户选择他们喜欢的设置。

  • “无” 以原始大小显示内容的原始分辨率。这可能会导致某些视频被裁剪或视频边缘的黑条。
  • Uniform 在保留纵横比和视频内容的同时,尽可能多地填充空间。 这可能会导致在视频的边缘出现水平和垂直黑色条。 这类似于宽屏模式。
  • UniformToFill 在保持纵横比的同时填充整个空间。 这可能会导致某些视频被裁剪。 这类似于全屏模式。
  • Fill 填充整个空间,但不保持纵横比。 不会裁剪任何视频,但可能会出现拉伸。 这类似于拉伸模式。

拉伸枚举值

在此处,使用 AppBarButton 循环访问 Stretch 选项。 语句 switch 检查 Stretch 属性的当前状态,并将其设置为枚举中的 Stretch 下一个值。 这使用户可以循环访问不同的拉伸状态。

<AppBarButton Icon="Trim"
              Label="Resize Video"
              Click="PictureSize_Click" />
private void PictureSize_Click(object sender, RoutedEventArgs e)
{
    switch (mediaPlayerElement.Stretch)
    {
        case Stretch.Fill:
            mediaPlayerElement.Stretch = Stretch.None;
            break;
        case Stretch.None:
            mediaPlayerElement.Stretch = Stretch.Uniform;
            break;
        case Stretch.Uniform:
            mediaPlayerElement.Stretch = Stretch.UniformToFill;
            break;
        case Stretch.UniformToFill:
            mediaPlayerElement.Stretch = Stretch.Fill;
            break;
        default:
            break;
    }
}

启用低延迟播放

MediaPlayerElement.MediaPlayer 上将 RealTimePlayback 属性设置为 true ,使媒体播放器元素能够减少播放的初始延迟。 这对于双向通信应用很重要,并且适用于某些游戏方案。 请注意,此模式将占用更多资源,并且能源效率较低。

此示例创建 一个 MediaPlayerElement 并将 RealTimePlayback 设置为 true

MediaPlayerElement mediaPlayerElement = new MediaPlayerElement();
mediaPlayerElement.MediaPlayer.RealTimePlayback = true;