我试图获得用户选择的音乐文件列表的自然持续时间。
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之后的自然持续时间保持不变(即设置为自动)并且没有timeSpan。为什么呢?
答案 0 :(得分:2)
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
,Opened
和Failed
事件处理程序使用EventArgs
作为事件参数类型,而不是RoutedEventArgs
和ExceptionRoutedEventArgs
,LoadedBehavior
。我做了这些修改,并确认MediaPlayer
的工作方式与我MediaElement
的工作方式大致相同,包括同样令人沮丧的表现。我怀疑MediaElement
正在使用MediaPlayer
。
如果您真正需要的媒体文件只是播放时间,那么使用MediaElement
或MediaPlayer
不是您的最佳选择。相反,像TagLib#这样的库(请参阅this answer)可以快速加载标签元数据(艺术家,专辑,标题,播放时间等),而无需读取整个媒体文件。
编辑:添加了关于MediaPlayer
的部分。
编辑2 :通常,要求Stack Overflow上的某人为您编写一些代码并不是一个好主意。但是,在这种情况下,我找不到TagLib#的任何文档;仅适用于它所基于的TagLib C ++库。所以这次我会例外。
如果您想使用TagLib#,您需要:
Install-Package taglib
。这会下载TagLib#并将其添加到您的项目中。添加以下代码以确定曲目播放时间(假设music
中有pMusic
个对象列表:
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
。这需要0.2秒才能在前面提到的同一组35个MP3文件上运行,这比使用MediaElement
要快得多。