【问题标题】:event MediaOpened unable to raised事件 MediaOpened 无法引发
【发布时间】:2013-12-31 12:03:55
【问题描述】:

我正在尝试获取用户选择的音乐文件列表的自然持续时间。

    MediaElement getDetails;    //class variable
    private void AddMusictoList(List<music> pMusic)
    {
        getDetails = new MediaElement();  

        getDetails.MediaOpened += getDetails_MediaOpened;
        getDetails.MediaFailed += getDetails_MediaFailed;
        getDetails.LoadedBehavior = MediaState.Manual;
        Uri path;
        list = new music();
        foreach (music iMusic in pMusic)
        {
            path = new Uri(iMusic.path);
            list = iMusic; 
            getDetails.Source = path;
            getDetails.Play();
        }
    }
    //the function below is not called.why?
    private void getDetails_MediaOpened(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hey");
        list.duration = getDetails.NaturalDuration.TimeSpan.ToString(@"hh\:mm\:ss");
        MessageBox.Show(list.duration);
        this.musicList.Items.Add(list);
    }


void getDetails_MediaFailed(object sender, ExceptionRoutedEventArgs e)
    {
        MessageBox.Show("Failed");
    }

媒体打开功能没有被提升。我已经调试了代码。 getDetails.Source 之后的自然持续时间保持不变(即设置为自动)并且没有时间跨度。为什么?

【问题讨论】:

  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。
  • 好的...下次我会注意的:)
  • 尝试为MediaElementMediaFailed 事件添加一个事件处理程序,看看它是否被调用。
  • @LukeWoodward:我已经更新了这个问题。我添加了 mediaFailed,但什么也没发生...没有持续时间和事件仍未引发(mediafailed 和 mediaopened)

标签: c# wpf


【解决方案1】:

MediaElement 没有播放媒体,因为它尚未添加到用户界面。您的MediaElement 在代码隐藏中创建并存储在私有字段中;我认为它永远不会对用户可见。

我在MediaElement documentation 中找不到任何说明这一点的内容,但我确实发现当我将媒体控件添加到Grid 时,它确实开始播放媒体。

但是,当 MediaElement 添加到 UI 时,会调用 Opened 事件处理程序,但仅针对最后一个媒体文件。这是因为加载媒体的过程发生在后台,而您的代码在此期间继续运行。因此,对于除最后一个以外的所有媒体文件,您最终会在 MediaElement 完成加载一个文件之前中断它,要求它加载下一个文件。

为了改进,您一次只能加载一个媒体文件,当每个加载完成后,开始加载下一个的过程:

    // New private field.  This enumerator stores our current position
    // iterating through the media files.
    private IEnumerator<music> files;

    private void AddMusictoList(List<music> pMusic)
    {
        this.files = pMusic.GetEnumerator();
        AddNextMusicFileIfExists();
    }

    // Starts loading the next file, if there is a next one.
    // If there isn't a next one, stops the media element.
    private void AddNextMusicFileIfExists()
    {
        if (this.files.MoveNext())
        {
            music iMusic = this.files.Current;
            Uri path = new Uri(iMusic.path);
            getDetails.Source = path;
            getDetails.Play();
        }
        else
        {
            getDetails.Stop();
        }
    }

    // I've replaced the MessageBoxes in this method with a call to
    // Debug.WriteLine, to prevent you getting spammed with lots of
    // message boxes.
    private void getDetails_MediaOpened(object sender, RoutedEventArgs e)
    {
        this.files.Current.duration = getDetails.NaturalDuration.TimeSpan.ToString(@"hh\:mm\:ss");
        System.Diagnostics.Debug.WriteLine(this.files.Current.duration);
        this.musicList.Items.Add(this.files.Current);
        AddNextMusicFileIfExists();
    }

这似乎有效,但速度有点慢。在一组 35 个 MP3 文件上运行大约需要 3.8 秒。这很可能是因为MediaElement 正在加载整个媒体文件并开始解码其中的一些数据,以及确定文件的播放时间。

另一种方法是使用MediaPlayer 类而不是MediaElement。这与MediaElement 的行为方式大致相同,但不是控件,因此无法添加到 XAML。

如果您希望使用MediaPlayer 而不是MediaElement,则需要对您的代码进行一些更改:

  • 你打电话给player.Open(uri)而不是getDetails.Source = uri
  • OpenedFailed 事件处理程序使用EventArgs 作为事件参数类型,而不是RoutedEventArgsExceptionRoutedEventArgs
  • 无需指定LoadedBehavior

我进行了这些更改并确认MediaPlayer 的工作方式与我让MediaElement 工作的方式大致相同,包括同样令人沮丧的性能。我怀疑MediaElement 在后台使用MediaPlayer

如果您真正需要的媒体文件只是其播放时间,那么使用MediaElementMediaPlayer 并不是您的最佳选择。相反,诸如 TagLib#(参见 this answer)之类的库可以快速加载标签元数据(艺术家、专辑、标题、播放时间等),而无需读取整个媒体文件。

编辑:添加了关于MediaPlayer的部分。

编辑 2:通常情况下,请 Stack Overflow 上的某个人为您编写一些代码并不是一个好主意。但是,在这种情况下,我找不到 TagLib# 的任何文档;仅适用于它所基于的 TagLib C++ 库。所以这次我会破例。

如果你想使用 TagLib#,你需要:

  • 在解决方案资源管理器中选择要添加 TagLib# 的项目。
  • 打开包管理器控制台(工具 > 库包管理器 > 包管理器控制台)并键入 Install-Package taglib。这会下载 TagLib# 并将其添加到您的项目中。
  • 添加如下代码以确定曲目播放时间(假设您在pMusic 中有一个music 对象列表:

        foreach (music iMusic in pMusic)
        {
            using (TagLib.File file = TagLib.File.Create(iMusic.path))
            {
                System.Diagnostics.Debug.WriteLine(
                        file.Properties.Duration.ToString(@"hh\:mm\:ss"));
            }
        }
    

这里我只是将输出写入Debug.WriteLine。在前面提到的同一组 35 个 MP3 文件上运行需要 0.2 秒,这比使用 MediaElement 快得多。

【讨论】:

  • 感谢您的回复。我会在一两天内试试这个。实际上有两个mediaElements(一个当前正在使用(播放),另一个一个一个加载文件(如果用户选择了多个文件)并获取音乐文件的详细信息并存储在List中)。问题是我已经调试了代码,即使列表中有一个元素,它也不会调用函数,我已经尝试过使用在 xaml 中声明的两个 mediaElements,比如第一个(第一个运行良好)
  • @SaadAbdullah:另一个选项是 MediaPlayer 类(请参阅上面我编辑的答案)。这不是一个控件,不能添加到 UI 中,而且它似乎可以工作。然而,它仍然有着和MediaElement一样惨淡的表现。如果你愿意,可以试试,但我仍然推荐 TagLib#。
  • 感谢 Luke 的回复,我试过 MediaPlayer,发现 mediaElement 好多了。你能解释一下 TagLib 吗?
  • @SaadAbdullah:我在回答中添加了关于 TagLib# 的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-10
相关资源
最近更新 更多