WPF MediElement 视频播放

WPF中可以使用MediaElement控件来进行音视频播放,然后需要做个进度条啥的,但是MediaElement.Position(进度)和MediaElement.NaturalDuration居然都不是依赖属性,简直不能忍!

好吧,首先说说比较传统的做法(winform?)

slider用来显示进度以及调整进度,tb1显示当前进度的时间值,tb2显示视频的时长。

player_Loaded 事件中使用DispatcherTimer来定时获取当前视频的播放进度,

player_MediaOpened 事件中获取当前视频的时长(只有在视频加载完成后才可以获取到)

slider_ValueChanged 事件中执行对视频进度的调整

xaml:
<MediaElement Name="player" Source="e:\MVVMLight (1).wmv" Loaded="player_Loaded" 
                      MediaOpened="player_MediaOpened"/>
        <Slider Grid.Row="1"  Margin="10" Name="slider" ValueChanged="slider_ValueChanged"/>
        <WrapPanel Grid.Row="1"  Margin="0,40,0,0">
            <TextBlock Name="tb1" Width="120"/>
            <TextBlock Name="tb2" Width="120"/>
        </WrapPanel>

后台代码:
private void player_Loaded(object sender, RoutedEventArgs e)
        {
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Tick += (ss, ee) =>
            {
                //显示当前视频进度
                var ts = player.Position;
                tb1.Text = string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);slider.Value = ts.TotalMilliseconds;
            };
            timer.Start();
        }

        private void player_MediaOpened(object sender, RoutedEventArgs e)
        {
            //显示视频的时长
            var ts = player.NaturalDuration.TimeSpan;
            tb2.Text = string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
            slider.Maximum = ts.TotalMilliseconds;
        }

        private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            //调整视频进度
            var ts = TimeSpan.FromMilliseconds(e.NewValue);
            player.Position = ts;
        }
View Code

下面再来看看使用依赖属性的做法:

添加一个类,继承MediaElement

  public class MediaElementExt : MediaElement, INotifyPropertyChanged
    {
        private DispatcherTimer timer;

        private const string CurrentTimeProperty = "CurrentTime";

        /// <summary>
        /// 当前播放进度
        /// </summary>
        public double CurrentTime
        {
            get
            {
                return this.Position.TotalMilliseconds;
            }
            set
            {
                //进度条拖动太频繁太久,性能跟不上,所以设置一个时间阀,跳过某些时段
                if ((DateTime.Now - _lastChangedTime).TotalMilliseconds > 50)
                {
                    this.Position = TimeSpan.FromMilliseconds(value);
                    _lastChangedTime = DateTime.Now;
                }
            }
        }

        /// <summary>
        /// 记录最后修改进度的时间,
        /// </summary>
        private DateTime _lastChangedTime = DateTime.Now;

        private const string DurationTimeProperty = "DurationTime";

        /// <summary>
        /// 当前视频时长
        /// </summary>
        public double DurationTime
        {
            get
            {
                if (this.NaturalDuration.HasTimeSpan)
                    return this.NaturalDuration.TimeSpan.TotalMilliseconds;
                return double.NaN;
            }
        }

        public MediaElementExt()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Tick += timer_Tick;

            this.MediaOpened += (ss, ee) =>
             {
                 //触发PropertyChanged DurationTime
                 this.RaisePropertyChanged(DurationTimeProperty);
                 timer.Start();
             };
            //发生错误和视频播放完毕 停止计时器
            this.MediaEnded += (ss, ee) => { timer.Stop(); };
            this.MediaFailed += (ss, ee) => { timer.Stop(); };
        }

        void timer_Tick(object sender, EventArgs e)
        {
            //定时触发PropertyChanged CurrentTime
            this.RaisePropertyChanged(CurrentTimeProperty);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
View Code
  <Grid DataContext="{Binding ElementName=player}">
        <Grid.RowDefinitions>
            <RowDefinition Height="8*"/>
            <RowDefinition Height="2*"/>
        </Grid.RowDefinitions>
        <!--<MediaElement/>-->
        <local:MediaElementExt  x:Name="player" Source="e:\MVVMLight (1).wmv" />
        <Slider Name="slider" Grid.Row="1" Value="{Binding CurrentTime,Mode=TwoWay}"  Maximum="{Binding DurationTime,Mode=OneWay}" Margin="10"/>
        <WrapPanel Grid.Row="1"  Margin="0,40,0,0">
            <TextBlock Text="{Binding Value, Converter={StaticResource TimeSpanConverter}, Mode=OneWay,ElementName=slider}" Width="120"/>
            <TextBlock Text="{Binding DurationTime, Mode=OneWay,Converter={StaticResource TimeSpanConverter}}" Width="120"/>
        </WrapPanel>
    </Grid>

---  xmlns:local="clr-namespace:WPF_Player" 
View Code
public class TimeSpanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            var ts = TimeSpan.FromMilliseconds((double)value);
            return string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
View Code

xaml中的转换器:

<local:TimeSpanConverter x:Key="TimeSpanConverter"/>

其实两种方法实现的都差不多,都需要计时器定时的去获取当前视频的进度,但第二种方面显然要优雅一些。

文章就写到这儿,如有疏漏错误,欢迎指正。

原文地址:https://www.cnblogs.com/LCHL/p/3084700.html