1. 前言
根据上级要求,使用uwp控件构建一个现有的媒体播放器,一开始听说这个要求我都蒙了,我第一感觉这个东西特别难写,应该涉及到一些视频格式解析的问题,详情看前bilibili员工dalao的项目:https://github.com/Bilibili/flv.js(可惜B站只给这个大佬开5000左右的工资把人家给逼走了
我觉得非常绝望,准备自杀了是,但是后来查了文档之后发现,其实人家都帮我们写好了ORZ,然后就没什么好说的了,做一个API调用者我觉得挺好=_=b
2. 预览
3. 代码分析
先看一下Mainpage.xaml
<Page
x:Class="MediaPlayerDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MediaPlayerDemo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Name="State" Grid.Column="1" Grid.Row="0"/>
<Button
Name="OpenBtn"
HorizontalAlignment="Center"
Content="Open the media file"
Grid.Row="1" Grid.Column="1"
Click="ButtonClickHandler"
/>
<MediaPlayerElement Name="MediaPlayer" AutoPlay="False" Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Grid.ColumnSpan="3" Visibility="Collapsed" AreTransportControlsEnabled="True" /> </Grid></Page>
这是这个项目主页面的配置文件看的出来,这个页面就只有三个控件,一个用来表示打开文件状态的textblock,一个用来播放媒体文件的主控件,还有一个选择文件的按钮
<Button
Name="OpenBtn"
HorizontalAlignment="Center"
Content="Open the media file"
Grid.Row="1" Grid.Column="1"
Click="ButtonClickHandler"
/>
其中,这个Click="ButtonClickHandler"是一个通向后端的一个接口,向后端一个名字为ButtonClickHandler的函数传递一个事件,用来触发接下来的函数。
而真正的主体,也就是我们的媒体播放器,在默认的情况下是隐藏的。
<MediaPlayerElement
Name="MediaPlayer" AutoPlay="False"
Grid.Row="0" Grid.Column="0"
Grid.RowSpan="3" Grid.ColumnSpan="3"
Visibility="Collapsed"
AreTransportControlsEnabled="True"
/>
其中,Visibility属性代表的是当前控件的显示状态,默认是Collapsed的,当我们使用OpenBtn的时候,触发后端的task函数,使之显示出来。
大致讲完了前端,我们看一下后端的C#代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Core;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板
namespace MediaPlayerDemo
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async System.Threading.Tasks.Task openMediaFile()
{
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".mp3");
fileOpenPicker.FileTypeFilter.Add(".mp4");
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
MediaPlayer.Source = MediaSource.CreateFromStorageFile(file);
MediaPlayer.Visibility = Visibility.Visible;
OpenBtn.Visibility = Visibility.Collapsed;
MediaPlayer.MediaPlayer.MediaEnded += new TypedEventHandler<Windows.Media.Playback.MediaPlayer, object>((player, resource) => {
OpenBtn.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
OpenBtn.Visibility = Visibility.Visible;
MediaPlayer.Visibility = Visibility.Collapsed;
});
});
}
else
{
State.Text = "unable to open the file!";
}
}
// action listener and handle the event
private void ButtonClickHandler(object sender, RoutedEventArgs e)
{
openMediaFile();
}
}
}
很简单的代码,其实我什么都没有做,仅仅只是调用了一下Windows自带的API,我们来看一下点击了OpenBtn之后发生了什么,首先,前端向后端发送了一个请求(object类型),并触发了一个事件,这个时候下列函数被调用:
// action listener and handle the event
private void ButtonClickHandler(object sender, RoutedEventArgs e)
{
openMediaFile();
}
这个代码调用了openMediaFile函数,那么openMediaFile函数到底做了什么呢?我们来看看:
private async System.Threading.Tasks.Task openMediaFile()
{
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".mp3");
fileOpenPicker.FileTypeFilter.Add(".mp4");
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
MediaPlayer.Source = MediaSource.CreateFromStorageFile(file);
MediaPlayer.Visibility = Visibility.Visible;
OpenBtn.Visibility = Visibility.Collapsed;
MediaPlayer.MediaPlayer.MediaEnded += new TypedEventHandler<Windows.Media.Playback.MediaPlayer, object>((player, resource) => {
OpenBtn.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
OpenBtn.Visibility = Visibility.Visible;
MediaPlayer.Visibility = Visibility.Collapsed;
});
});
}
else
{
State.Text = "unable to open the file!";
}
}
首先需要解决的问题是,文件的来源,我怎么从磁盘中选择一个合适的文件读取到媒体播放器中呢
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".mp3");
fileOpenPicker.FileTypeFilter.Add(".mp4");
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
前面的五行初始化了一个文件选择视图,第二到第五行初始化了这个选择器,指定了选择文件的后缀格式,指派了选择器风格, 最后一行是神来之笔,file是文件对象,能够等到用户选择完毕之后再初始化。
if (file != null)
{
// Application now has read/write access to the picked file
MediaPlayer.Source = MediaSource.CreateFromStorageFile(file);
MediaPlayer.Visibility = Visibility.Visible;
OpenBtn.Visibility = Visibility.Collapsed;
MediaPlayer.MediaPlayer.MediaEnded += new TypedEventHandler<Windows.Media.Playback.MediaPlayer, object>((player, resource) => {
OpenBtn.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
OpenBtn.Visibility = Visibility.Visible;
MediaPlayer.Visibility = Visibility.Collapsed;
});
});
}
else
{
State.Text = "unable to open the file!";
}
当file对象被初始化之后task函数会检查file是否被成功打开,如果file对象存在,那么设置媒体播放器的播放源为file,将前端的媒体播放器设置为可见,并且将选择文件的按钮隐藏起来,并且监听一个播放结束的事件,当媒体文件播放结束之后,重新显示选择文件的按钮,隐藏媒体播放器。
至此,整块的代码就分析完毕了
4. 引用来源(抄了很多代码,很惭愧
// 实现fileOpenPicker的Demo
https://docs.microsoft.com/zh-cn/uwp/api/windows.storage.pickers.fileopenpicker#examples
// 监听媒体播放结束事件的Demo
https://docs.microsoft.com/zh-cn/uwp/api/windows.ui.xaml.controls.mediaplayerelement#handle-media-events
// C# 事件(Event)
http://www.runoob.com/csharp/csharp-event.html
// UI线程相关
http://edi.wang/post/2016/2/18/windows-10-uwp-async-await-ui-thread