近二日闲来无事,把silverlight的官方文档瞅了瞅,倒腾了一个简单的视频播放器,顺便也测试了下能否播放传说中的h.264,最终效果如下:
http://images.24city.com/jimmy/player/default.html
布局思路:
Grid做为最外层容器,分上中下三行
第一行为视频播放窗口,同时单击视频时"暂停"遮罩层也放在这一行,只不过默认不显示而已 第二行为进度条显示区,为了方便布局,在这一行用StackPanel作子容器横向放置了二个控件(进度条和时间显示) 第三行为其它的控制按钮区,也是用StackPanel横向放置其它控件
实现的功能:
1.单击视频,暂停播放,再次单击则继续播放,原则就是利用鼠标单击事件控制Canvas的显示/隐藏以及调用MediaElement的Play(),Pause()方法 2.进度条与播放时间的同步,这里用到了Timer控件,每隔一定时间重新设置进度条的值 3.播放列表采用json字符串解析后绑定实现,同时选择列表的相关视频后,马上播放选择的视频 4.全屏功能 5.静音功能(其实还可以方便左右声道功能,只要不知道界面上怎么放,所以这一块功能没加上去) 6.缓冲以及加载进度的百分比进度显示 7.播放时,预先加载下一段视频(这一块好象效果不明显,主要是对silverlight的缓冲机制不清楚,期待大家共同探讨改进)
另:本示例中用的视频全部为mp4格式的h.264视频
其它不清楚的地方,基本上代码中都有注释
xaml代码:
代码语言:js复制<UserControl x:Class="Test.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
mc:Ignorable="d">
<Grid x:Name="LayoutRoot" ShowGridLines="False" >
<!--Grid布局:分成三行,第一行放视频窗口,第二行为进度条,第三行为其它控制按钮-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="22"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
</Grid.RowDefinitions>
<!--视频播放控件-->
<MediaElement x:Name="media" Source="" Grid.Row="0" Grid.Column="0" CurrentStateChanged="Media_State_Changed" MediaEnded="media_MediaEnded" Cursor="Hand" MouseLeftButtonDown="media_MouseLeftButtonDown" BufferingProgressChanged="media_BufferingProgressChanged" DownloadProgressChanged="media_DownloadProgressChanged"></MediaElement>
<!--这里用一个Canvas来实现暂停时的遮盖效果-->
<Canvas Background="#AAFAEBD7" Grid.Row="0" Grid.Column="0" Cursor="Hand" x:Name="canvas_Pause" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" >
<Ellipse Height="200" Width="200" Stroke="AliceBlue" StrokeThickness="10" Canvas.Left="140" Canvas.Top="80"></Ellipse>
<Path Stretch="Fill" Stroke="AliceBlue" StrokeThickness="10" Height="98" Width="10" UseLayoutRounding="False" Canvas.Left="203" Canvas.Top="131" Data="M208,136 L208,224"/>
<Path Stretch="Fill" Stroke="AliceBlue" StrokeThickness="10" Height="98" Width="10" UseLayoutRounding="False" Canvas.Left="263" Canvas.Top="131" Data="M208,136 L208,224"/>
<TextBlock Canvas.Left="104" Canvas.Top="296" Foreground="White" >Made by 菩提树下的杨过(http://yjmyzz.cnblogs.com/)</TextBlock>
</Canvas>
<MediaElement x:Name="mediaBuffer" Width="0" Grid.Column="0" Grid.Row="0" BufferingTime="0:0:10" IsMuted="True" AutoPlay="True" ></MediaElement>
<!--第二行用一个StackPanel横向放了二个子控件,用于显示进度条和当前播放时间-->
<StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" >
<Slider Height="20" Width="370" x:Name="sliderProgress" ValueChanged="sliderProgress_ValueChanged" Cursor="Hand" ></Slider>
<TextBlock Text="00:00:00/00:00:00" Width="110" x:Name="txtTime"/>
</StackPanel>
<!--第三行同样用StackPanel横向放置其它控制按钮-->
<StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" >
<Button Click="PlayMedia" Content="||" Width="25" Height="25" x:Name="btnPlay" Cursor="Hand" />
<Button Click="StopMedia" Content="■" Width="25" Height="25" x:Name="btnStop" Cursor="Hand"/>
<TextBlock x:Name="txtProgress" FontSize="12" Width="90" Text="缓冲中100%" Height="25" TextAlignment="Center" Margin="3,0" Padding="0,6,0,0" ></TextBlock>
<ComboBox x:Name="cboList" SelectionChanged="cboList_SelectionChanged" Height="25" Width="232">
</ComboBox>
<Button Content="静" Width="25" Height="25" Margin="3,0" x:Name="btnVoice" Click="btnVoice_Click" Cursor="Hand"></Button>
<Slider Height="25" Width="50" x:Name="sliderVoice" Maximum="1" Minimum="0" ValueChanged="sliderVoice_ValueChanged" Value="0.5" Cursor="Hand" ></Slider>
<Button Content="全" Width="20" Height="25" Cursor="Hand" x:Name="btnFull" Click="btnFull_Click"></Button>
</StackPanel>
</Grid>
</UserControl>
后端cs代码:
代码语言:js复制 using System;
using System.Collections.Generic;
using System.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Threading;
namespace Test
{
public partial class MainPage : UserControl
{
private DispatcherTimer _timerPlay;
//实际应用中,以下字符串可通过wcf调用获得
private string _medialist = "[{src:'http://task.24city.com/video/01.mp4',name:'苹果王手机第1段'},{src:'http://task.24city.com/video/02.mp4',name:'苹果王手机第2段'},{src:'http://task.24city.com/video/03.mp4',name:'苹果王手机第3段'},{src:'http://task.24city.com/video/04.mp4',name:'蔡依林-柠檬草的味道'},{src:'http://task.24city.com/video/05.mp4',name:'我也不知道是啥'},{src:'http://task.24city.com/video/06.mp4',name:'游戏宣传片段'}]";
List<MediaItem> _listMedia = null;
int _currentIndex = -1;//正在播放的元素索引
public MainPage()
{
InitializeComponent();
//解析Json
JsonValue _jsValue = JsonArray.Parse(_medialist);
if (_jsValue.Count > 0)
{
_listMedia = new List<MediaItem>(_jsValue.Count);
for (int i = 0; i < _jsValue.Count; i )
{
_listMedia.Add(new MediaItem() { src = _jsValue[i]["src"], name = _jsValue[i]["name"] });
}
_currentIndex = 0;//默认从第一个开始播放
media.Source = new Uri(_listMedia[_currentIndex].src);
cboList.ItemsSource = _listMedia;
cboList.DisplayMemberPath = "name";
cboList.SelectedIndex = _currentIndex;
this._timerPlay = new DispatcherTimer();
this._timerPlay.Interval = new TimeSpan(0, 0, 0, 0, 100);
this._timerPlay.Tick = new EventHandler(timer_Tick);
this._timerPlay.Start();
}
App.Current.Host.Content.FullScreenChanged = new EventHandler(Content_FullScreenChanged);
}
/**//// <summary>
/// timer触发时,设置进度条与播放时间同步
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Tick(object sender, EventArgs e)
{
this.sliderProgress.Maximum = this.media.NaturalDuration.TimeSpan.TotalSeconds;
this.sliderProgress.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(sliderProgress_ValueChanged);
this.sliderProgress.Value = this.media.Position.TotalSeconds;
this.sliderProgress.ValueChanged = new RoutedPropertyChangedEventHandler<double>(sliderProgress_ValueChanged);
this.txtTime.Text = media.Position.Hours.ToString().PadLeft(2, '0') ":" media.Position.Minutes.ToString().PadLeft(2, '0') ":" media.Position.Seconds.ToString().PadLeft(2, '0') "/" media.NaturalDuration.TimeSpan.Hours.ToString().PadLeft(2, '0') ":" media.NaturalDuration.TimeSpan.Minutes.ToString().PadLeft(2, '0') ":" media.NaturalDuration.TimeSpan.Seconds.ToString().PadLeft(2, '0');
}
/**//// <summary>
/// 显示播放状态
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Media_State_Changed(object sender, EventArgs e)
{
if (_currentIndex >= 0)
{
MediaItem _currentMedia = _listMedia[_currentIndex];
switch (media.CurrentState)
{
case System.Windows.Media.MediaElementState.AcquiringLicense:
txtProgress.Text = "加载许可证";
break;
case System.Windows.Media.MediaElementState.Buffering:
txtProgress.Text = "缓冲中";
break;
case System.Windows.Media.MediaElementState.Closed:
txtProgress.Text = "已关闭";
break;
case System.Windows.Media.MediaElementState.Individualizing:
txtProgress.Text = "加载个性化设置";
break;
case System.Windows.Media.MediaElementState.Opening:
txtProgress.Text = "加载中";
break;
case System.Windows.Media.MediaElementState.Paused:
txtProgress.Text = "已暂停";
break;
case System.Windows.Media.MediaElementState.Playing:
txtProgress.Text = "正在播放";
//预选缓冲下一段视频(不过在实际测试中,感觉这么干没啥明显效果,欢迎大家共同探讨如何提前缓冲下一段视频)
if (_currentIndex 1 > _listMedia.Count - 1)
{
mediaBuffer.Source = new Uri(_listMedia[0].src);
}
else
{
mediaBuffer.Source = new Uri(_listMedia[_currentIndex 1].src);
}
break;
case System.Windows.Media.MediaElementState.Stopped:
txtProgress.Text = "已停止";
break;
default:
break;
}
}
}
/**//// <summary>
/// 停止播放
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StopMedia(object sender, System.Windows.RoutedEventArgs e)
{
media.Stop();
btnPlay.Content = ">";
}
/**//// <summary>
/// 播放/暂停
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PlayMedia(object sender, System.Windows.RoutedEventArgs e)
{
if (media.CurrentState == System.Windows.Media.MediaElementState.Paused || media.CurrentState == System.Windows.Media.MediaElementState.Stopped)
{
media.Play();
btnPlay.Content = "||";
canvas_Pause.Visibility = Visibility.Collapsed;
}
else
{
media.Pause();
btnPlay.Content = ">";
canvas_Pause.Visibility = Visibility.Visible;
}
}
/**//// <summary>
/// 一段播放完毕后,自动跳到下一段
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void media_MediaEnded(object sender, System.Windows.RoutedEventArgs e)
{
_currentIndex ;
if (_currentIndex > _listMedia.Count - 1)
{
_currentIndex = 0;
}
media.Source = new Uri(_listMedia[_currentIndex].src);
cboList.SelectedIndex = _currentIndex;
}
/**//// <summary>
/// 下拉列表改变时,播放相关片段
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cboList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_currentIndex = cboList.SelectedIndex;
if (_currentIndex > _listMedia.Count - 1)
{
_currentIndex = 0;
}
media.Source = new Uri(_listMedia[_currentIndex].src);
canvas_Pause.Visibility = System.Windows.Visibility.Collapsed;
media.Position = new TimeSpan(0,0,0,0,0);
sliderProgress.Value = 0;
}
/**//// <summary>
/// 暂时Canvas点击后,隐藏Canvas,同时继续播放
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
canvas_Pause.Visibility = System.Windows.Visibility.Collapsed;
media.Play();
}
/**//// <summary>
/// 视频画面单击时,暂时播放
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void media_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
canvas_Pause.Visibility = System.Windows.Visibility.Visible;
media.Pause();
}
/**//// <summary>
/// 进度条拖动时,跳到相关位置
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void sliderProgress_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<double> e)
{
this.media.Position = new TimeSpan((long)(e.NewValue * 1000 * 1000 * 10));
}
//private void media_BufferingProgressChanged(object sender, RoutedEventHandler e)
//{
// txtTime.Text = "缓冲中" media.BufferingProgress.ToString("F0") "%";
//}
/**//// <summary>
/// 静音按钮的实现
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnVoice_Click(object sender, RoutedEventArgs e)
{
if (media.IsMuted)
{
media.IsMuted = false;
this.btnVoice.Content = "静";
sliderVoice.IsEnabled = true;
}
else
{
media.IsMuted = true;
this.btnVoice.Content = "音";
sliderVoice.IsEnabled = false;
}
}
/**//// <summary>
/// 调整音量大小
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void sliderVoice_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (sliderVoice == null)
{
return;
}
media.Volume = sliderVoice.Value;
}
private void btnFull_Click(object sender, RoutedEventArgs e)
{
Content contentObj = App.Current.Host.Content;
contentObj.IsFullScreen = !contentObj.IsFullScreen;
}
private void Content_FullScreenChanged(object sender, EventArgs e)
{
Content contentObj = App.Current.Host.Content;
if (contentObj.IsFullScreen)
{
btnFull.Content = "退";
}
else
{
btnFull.Content = "全";
}
}
/**//// <summary>
/// 显示缓冲进度
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void media_BufferingProgressChanged(object sender, RoutedEventArgs e)
{
this.txtProgress.Text = "缓冲中" (media.BufferingProgress*100).ToString("F0") "%";
canvas_Pause.Visibility = Visibility.Visible;
if (media.BufferingProgress >= 1.0)
{
canvas_Pause.Visibility = Visibility.Collapsed;
switch (media.CurrentState)
{
case System.Windows.Media.MediaElementState.Buffering:
txtProgress.Text = "缓冲中";
break;
case System.Windows.Media.MediaElementState.Closed:
txtProgress.Text = "已关闭";
break;
case System.Windows.Media.MediaElementState.Paused:
txtProgress.Text = "已暂停";
break;
case System.Windows.Media.MediaElementState.Playing:
txtProgress.Text = "正在播放";
break;
case System.Windows.Media.MediaElementState.Stopped:
txtProgress.Text = "已停止";
break;
default:
txtProgress.Text = "就绪";
break;
}
}
}
/**//// <summary>
/// 显示加载进度
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void media_DownloadProgressChanged(object sender, RoutedEventArgs e)
{
txtProgress.Text = "加载中" (media.DownloadProgress * 100).ToString("F0") "%";
if (media.DownloadProgress >= 1)
{
switch (media.CurrentState)
{
case System.Windows.Media.MediaElementState.Buffering:
txtProgress.Text = "缓冲中";
break;
case System.Windows.Media.MediaElementState.Closed:
txtProgress.Text = "已关闭";
break;
case System.Windows.Media.MediaElementState.Paused:
txtProgress.Text = "已暂停";
break;
case System.Windows.Media.MediaElementState.Playing:
txtProgress.Text = "正在播放";
break;
case System.Windows.Media.MediaElementState.Stopped:
txtProgress.Text = "已停止";
break;
default:
txtProgress.Text = "就绪";
break;
}
}
}
}
/**//// <summary>
/// 媒体元素Item自定义类
/// </summary>
public class MediaItem
{
public string src { set; get; }
public string name { set; get; }
}
}