使用MediaElement + Storyboard连续播放视频

本文关键字:播放 视频 连续 Storyboard MediaElement 使用 | 更新日期: 2023-09-27 18:08:45

我有一个连续录制的视频文件的时间轴。我需要在我的应用程序中以相同的顺序播放它们。

  1. 我已经知道每个视频的相对开始时间和持续时间。
  2. 下一个视频可能在前一个视频停止录制几秒钟、几分钟甚至几小时后才开始录制。
  3. 我需要某种位置更改通知,以便我可以同步其他UI元素到视频位置(例如图形)。
  4. 在没有录制视频的部分,视频窗口将显示空白屏幕。
  5. 不幸的是,我现在使用WinForms卡住了,但是我在ElementHost中嵌入了MediaElement

看来MediaTimeline + Storyboard的组合很适合我的需要。Storyboard提供满足条件3的CurrentTimeInvalidated事件。对于条件1和条件2,我相信我可以为每一个视频创建一个MediaTimeline,并将它们每一个作为一个孩子添加到Storyboard中。看来我已经部分修好了,但是还有一些问题。

在我目前的实现中,故事板从头到尾播放得很好。然而,视频只显示最后添加到故事板的视频。

下面是我想实现的视频时间轴播放器的一些简化伪代码。

public class VideoTimelineEntry
{
    public Uri Uri;
    public TimeSpan RelativeStartTime;
    public TimeSpan Duration;
}
public class VideoTimelinePlayer : System.Windows.Forms.UserControl
{
    private MediaElement _mediaElement = ...; // Contained in ElementHost
    private Storyboard _storyboard = new Storyboard();
    public void LoadTimeline(IEnumerable<VideoTimelineEntry> entries)
    {
        foreach (VideoTimelineEntry entry in entries)
        {
            MediaTimeline mediaTimeline = new MediaTimeline
            {
                BeginTime = entry.RelativeStartTime,
                Duration  = new Duration(entry.Duration),
                Source    = entry.Uri
            };
            _storyboard.Children.Add(mediaTimeline);
            // I think this is my problem. How do I set the target
            // so that it is always playing the current video, and
            // not just the last one in my timeline?
            Storyboard.SetTarget(mediaTimeline, _mediaElement);
        }
    }
    public void Play()
    {
        _storyboard.Begin();
    }
    public void Pause()
    {
        _storyboard.Pause();
    }
    public void Stop()
    {
        _storyboard.Stop();
    }
}

使用MediaElement + Storyboard连续播放视频

似乎每个MediaTimeline只能针对1个MediaElement。作为一种解决方法,我现在为每个MediaTimeline创建一个专用的MediaElement。在我看来,这不是一个很好的解决方案,但我想不出更好的方法,除非我想处理轮询/定时和动态更改视频源。然而,我使用Storyboard的全部原因是为了避免这样做。

更新7/24/14

我决定贴一个很淡的例子来改进这个答案。

public void LoadTimeline(IEnumerable<MediaTimeline> mediaTimelines)
{
    // Check that none of the timelines overlap as specified by the
    // acceptance criteria.
    // e.g. timeline2.BeginTime < timeline1.BeginTime + timeline1.Duration.
    _storyboard.Children.Clear();
    foreach (MediaTimeline mediaTimeline in mediaTimelines)
    {
        _storyboard.Children.add(mediaTimeline);
        MediaElement mediaElement = new MediaElement();
        // _grid is just an empty <Grid></Grid> in the xaml layer.
        _grid.Children.Add(mediaElement);
        // Each media timeline now targets a dedicated media element.
        Storyboard.SetTarget(mediaTimeline, mediaElement);
        // Bring the active media element to the top.
        mediaTimeline.CurrentStateInvalidated += (sender, args) =>
        {
            Panel.SetZIndex(mediaElement, int.MaxValue);
        };
    }
}