切换mediaElements视频

时间:2014-01-07 08:57:33

标签: c# .net wpf video media

我正在制作一个视频项目。在该项目中,将为用户播放视频,如果用户单击按钮,则视频将更改为下一个视频。点是,下一个视频将从前一个视频停止的点开始播放(因此,如果用户在00:00:30按下NEXT按钮,则下一个视频将从该点播放)。

我面临的问题是,在下一个视频播放之前总会有一些黑屏,我希望更改能够顺利进行,而不会让用户观看黑屏一两秒。

到目前为止,我已尝试使用一个mediaElement和两个mediaElements来解决它:

单个mediaElement更改代码:

TimeSpan Time = mediaElement1.Position;
mediaElement1.Source = new Uri("NEXT VIDEO LOCATION"); 
mediaElement1.Position = Time; 
mediaElement1.Play();

两个mediaElement代码:

TimeSpan Time = mediaElement1.Position;
mediaElement2.Source = new Uri("NEXT VIDEO LOCATION");
mediaElement2.Position = Time;
mediaElement1.Visibility = Visibility.Hidden;
mediaElement1.Source = null;
mediaElement2.Visibility = Visibility.Visible;
mediaElement2.Play();

两次尝试都没有视频之间的平滑切换。提前感谢您对此事的任何启示。

2 个答案:

答案 0 :(得分:2)

对不起,如果我不是那么清楚。实际上MediaElement具有Position属性,但它不是DependancyProperty且不是Bindable。也无法直接使用事件检索更新的位置。所以我很久以前使用的方式是在StoryBoard中使用MediaTimeLine。您应该查看Microsoft的这篇文章How to: Control a MediaElement by Using a Storyboard。 我用这种方式使用MediaElement在视频文件中搜索。另一种方法是使用名为WPFMediaKit的第三方库,这非常有用。你可以在 WPF MediaKit - For webcam, DVD and custom video support in WPF

找到它

第一个例子:使用WPFMediaKit(没有实际播放MP4文件的方法......)

WPF MediaKit中的MediaUriElement允许您绑定其MediaPosition,因为它是DependancyPropertyMediaUriElement是.Net MediaElement的包装,其功能不佳。所以,参考我的第一个答案,我写了一个非常简单的POCO。你可以这样写:

<强> MainWindow.xaml

<Window x:Class="Media.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:media="clr-namespace:WPFMediaKit.DirectShow.Controls;assembly=WPFMediaKit"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <Storyboard x:Key="SwitchToSecondPlayerStoryboard">
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="firstPlayer">
            <DiscreteObjectKeyFrame KeyTime="0:0:0.7" Value="{x:Static Visibility.Collapsed}"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="secondPlayer">
            <DiscreteObjectKeyFrame KeyTime="0:0:0.7" Value="{x:Static Visibility.Visible}"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</Window.Resources>
<Window.Triggers>
    <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="switchButton">
        <BeginStoryboard Storyboard="{StaticResource SwitchToSecondPlayerStoryboard}"/>
    </EventTrigger>
</Window.Triggers>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="60"/>
    </Grid.RowDefinitions>
    <media:MediaUriElement x:Name="firstPlayer" Source="firstFile.wmv" LoadedBehavior="Play"/>
    <media:MediaUriElement x:Name="secondPlayer" Source="secondFile.wmv" MediaPosition="{Binding Path=MediaPosition, ElementName=firstPlayer}" Visibility="Collapsed" Volume="0"/>

    <Button Grid.Row="1" x:Name="switchButton" Content="SWITCH NEXT FILE" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnSwitchButtonClick"/>
</Grid>

这是 MainWindow.xaml.cs

namespace Media
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnSwitchButtonClick(object sender, RoutedEventArgs e)
        {
            //Do whatever you want with the players.
        }
    }
}

此程序有效,没有“空白屏幕”效果。您只需要改进Storyboard转换(具有FadeIn效果的示例),但这是原则。 您还会注意到第二个MediaPosition绑定到第一个MediaPosition。 这意味着要始终检查MediaPosition是否低于文件的Duration以避免错误,但这只是代码内容。

请注意,我简化了代码,避免使用MVVM和其他架构,以便更清晰。

第二个解决方案:使用MediaElement

进行解决方法

<强> MainWindow.xaml

<Window x:Class="Media.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Storyboard x:Key="ShowSecond">
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="firstMediaElement">
                <DiscreteObjectKeyFrame KeyTime="0:0:0.3" Value="{x:Static Visibility.Collapsed}"/>
            </ObjectAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="secondMediaElement">
                <DiscreteObjectKeyFrame KeyTime="0:0:0.3" Value="{x:Static Visibility.Visible}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Window.Triggers>
        <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="btnlaunchNext">
            <BeginStoryboard Storyboard="{StaticResource ShowSecond}"/>
        </EventTrigger>
    </Window.Triggers>
    <Grid>
        <StackPanel Background="Black">

            <MediaElement Name="firstMediaElement" LoadedBehavior="Manual" Source="firstFile.mp4" Width="260" Height="150" Stretch="Fill" />
            <MediaElement Name="secondMediaElement" Volume="0" LoadedBehavior="Manual" Source="secondFile.mp4" Visibility="Collapsed" Width="260" Height="150" Stretch="Fill" />

            <!-- Buttons to launch videos. -->
            <Button x:Name="btnlaunch" Content="PLAY FIRST" Click="OnLaunchFirstButtonClick"/>
            <Button x:Name="btnlaunchNext" Content="PLAY NEXT" Click="OnLaunchNextButtonClick"/>

        </StackPanel>
    </Grid>
</Window>

<强> MainWindow.xaml.cs

using System;
using System.Timers;
using System.Windows;

namespace Media
{
    /// <summary>
    /// Interaction logic for pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public TimeSpan Position { get; set; }

        Timer _updateTimer;

        public MainWindow()
        {
            InitializeComponent();

            //Timer interval fixed to 1 second to read the actual position of the first media element
            this._updateTimer = new Timer(1000);
            this._updateTimer.Elapsed += this.UpdateTimerElapsed;
        }

        //The callback of your update timer
        private void UpdateTimerElapsed(object sender, ElapsedEventArgs e)
        {
            //I use the dispatcher because you call the Position from another thread so it has to be synchronized
            Dispatcher.Invoke(new Action(() => this.Position = firstMediaElement.Position));
        }

        //When stopping the first, you start the second and set its Position
        private void OnLaunchNextButtonClick(object sender, RoutedEventArgs e)
        {
            this.firstMediaElement.Stop();

            this.secondMediaElement.Volume = 10;
            this.secondMediaElement.Play();
            this.secondMediaElement.Position = this.Position;
        }

        //When you start the first, you have to start the update timer
        private void OnLaunchFirstButtonClick(object sender, RoutedEventArgs e)
        {
            this.firstMediaElement.Play();
            this._updateTimer.Start();
        }
    }
}

这是我能提供的唯一简单解决方案。有不同的方法可以做到这一点,特别是使用MediaTimeline,但是对于您所面临的问题,实现并不像看起来那么容易。第二个例子,第一个例子是完全编译和运行,并以我想要做的为目标。

希望这会有所帮助。请随时向我提供反馈。

答案 1 :(得分:0)

有没有办法预加载下一个视频?如果是这种情况,您可以尝试在每个MediaElement上加载两个源,然后在播放时将第一个Position绑定到第二个MediaElement。 默认情况下,第二个CollapsedHidden(最好是visible关于使用大量图形元素时的效果)并且将成为Source且其Position已加载,其MediaElement已经绑定在第一个{{1}}位置。

希望这有帮助。