闲来无事,倒腾了一个简单的silverlight视频播放器

近二日闲来无事,把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代码:

<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代码:

  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; }
   }
 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏电光石火

Markdown 语法手册 (完整整理版)

1. 斜体和粗体 代码: *斜体*或_斜体_ **粗体** ***加粗斜体*** ~~删除线~~ 显示效果: ...

1K120
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(55)-工作流设计-表单布局

前言:这一节比较有趣。基本纯UI,但是不是很复杂 有了实现表单的打印和更加符合流程表单方式,我们必须自定义布局来适合业务场景打印!我们想要什么效果?看下图 (我...

23290
来自专栏Golang语言社区

go语言实现通过FTP库自动上传web日志

因为平时管理的web服务器都是VM服务器,为节省硬盘空间,一般给虚拟机分配的硬盘空间都比较小,只有8G,因为,保存不了多少日志,所以每天都需要把每台WEB日志转...

44730
来自专栏林德熙的博客

win10 uwp 按下等待按钮

我们需要一个值让我们知道是不是已经完成了后台,按钮可以按下,在按下时,自动让按钮IsEnable为false。

6920
来自专栏Golang语言社区

Golang语言 上传文件

import ( "bufio" "flag" "fmt" "io" "log" "os" "os/ex...

37380
来自专栏Python小屋

Python数据可视化案例三:使用Slider组件调整曲线参数

首先更正一个地方,昨天的文章Pythonic:递归、回溯等5种方法生成不重复数字整数中最后一段代码的注释中不小心把“排列”写成了“组合”,抱歉抱歉! 今天的内容...

34560
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(57)-插件---ueditor使用

目录: 前言 开发环境 知识点 初始使用 自定义工具栏 设置和读取编辑器内容 文件上传 ueditor加水印 -------------------------...

37750
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(46)-工作流设计-设计分支

步骤设置完毕之后,就要设置好流转了,比如财务申请大于50000元(请假天数>5天)要总经理审批,否则财务审批之后就结束了。 ? 设置分支没有任何关注点,我们把关...

37070
来自专栏谦谦君子修罗刀

react-native-swiper组件-横扫你的轮播图

一念起,万水千山。一念灭,沧海桑田。 许久不曾召幸React Native爱妃,未曾想一见竟让寡人目瞪口呆。啥~~~你就说你买包包的速度能跟上你版本迭代更新的...

69660
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(86)-日程管理-fullcalendar插件用法

前言 本文分享fullcalendar用法,最后面提供代码下载 说到日程管理,基于JQuery的插件FullCalendar当之无愧,完整的API稳定和调...

436100

扫码关注云+社区

领取腾讯云代金券