Managed DirectX9 的 AudioVideoPlayback 命名空间中的问题






4.68/5 (35投票s)
关于为什么不使用DirectX9进行音视频播放的众多原因的导览
引言
使用DirectX 9,终于可以在不使用互操作的情况下从C#中使用DirectX了。这包括AudioVideoPlayback
命名空间,顾名思义,它用于播放音频和视频文件。它主要包含两个对象:一个Audio
类和一个Video
类(包含一个Audio
属性)。接口看起来非常简单,因此您很可能很快就决定编写一个视频播放器就像这样简单
if (browseForFile)
{
if (video == null)
video = new Video(filePath);
else
video.Open(filePath);
video.Owner = videoPanel;
video.Size = videoPanel.Size;
video.Ending +=new EventHandler(video_Ending);
video.Play();
}
不幸的是,这段代码几乎每一行都有问题。本文的目的是解释DirectX 9中AudioVideoPlayback
的陷阱,以及我发现的解决这些问题的方法。我将逐行解释,将上面的示例改编成一个能够可靠工作的程序。
构造函数
从很多方面来说,第一个问题是最糟糕的。Video
类提供了一个Open
方法,但不提供无参构造函数。换句话说,您必须用一个文件来构建一个Video
对象,然后您有一个方法可以使用同一个对象打开其他视频。它会疯狂地泄漏内存。为了重用Video
对象,您需要先销毁它,如下所示:
if (video != null)
{
video.Stop();
video.Dispose();
video = null;
}
video = new Video(filePath);
是的,您需要强制销毁Video
对象,并创建一个新的。我不知道为什么会这样,或者如何充分解释它(尤其考虑到微软为代码“托管”所做的巨大努力)。我绝对建议您永远不要使用Open
方法。
所有者和大小
设置视频的所有者是可以的,不太可能出错,只要您设置正确。在开发过程中,我遇到了一些非常奇怪的行为,但只要我解决了代码中的问题,这些行为就消失了。大小是另一回事。您可以设置视频的大小,它会以该大小正常播放。但是一旦视频结束,它正在播放的控件就会自动调整大小为视频的原始分辨率。要解决这个问题,您需要在视频播放的控件上设置一个OnSize
事件处理程序,并手动将控件调整到所需的大小,这个大小可以存储为一个属性,或者为可调整大小的窗口计算,或者如果您应用程序是顶层全屏的(这样屏幕分辨率就不会改变,应用程序也不会被调整大小),您可以编写代码来阻止窗口缩小。在示例应用程序中,看起来是这样的:
private void videoPanel_SizeChanged(object sender, EventArgs e)
{
videoPanel.Size.Width = this.Size.Width - 10;
videoPanel.Size.Height = this.Size.Height - 10;
}
捕捉播放结束
当我看到Video
对象有一个文件结束事件处理程序时,我认为这似乎非常合乎逻辑和有用。确实如此,而且有时它确实会被调用。Audio
对象的事件似乎总是会被触发,除非它是Video
对象上的Audio
属性,但视频就是不能一致地工作。所以我设置了一个单独的计时器,试图自己弄清楚发生了什么,就像这样:
private void videoTimer(object sender, EventArgs e)
{
if (!video.Playing)
OnVideoEnd(sender, e);
}
不幸的是,即使视频停止了,Playing
属性仍然是'true
'。所以,我不得不这样做:
private void videoTimer(object sender, EventArgs e)
{
if (video.CurrentPosition >= video.Duration)
OnVideoEnd(sender, e);
}
CurrentPosition
似乎总是超过Duration
,即使Playing
属性仍然是true
,并且Ending
事件经常不触发。
我的音频有多响?
Video
的Audio
属性包含与Audio
类相同的所有方法,但它们作用于视频剪辑中的音频。对于我的应用程序,我需要在第二个监视器上播放剪辑,并在主监视器上提供预览。所以我决定播放两次视频,并在其中一个上关闭声音。Audio
类有一个Volume
属性,它是一个整数。我尝试将其设置为0,但似乎没有任何效果。我阅读了一些文档,它们说0是静音,10000是开启,所以我尝试了一下,结果崩溃了。最后,我在MSDN上找到了另一个代码片段,它(正确地)告诉我,-10000是静音。我确定这个属性被称为Volume
是有某种深奥的原因,0是完全开启,而-10000(为什么不是-100000,或者-94567547???)是静音。尽管如此,它就是这样工作的。
不仅仅是视频?
如果您想使用一个面板来显示静态图像和视频,这可能会起作用,只要您先销毁视频对象。但我没有成功,视频的最后一帧总是会保留,并且我无法在其上绘制。如果您创建一个自定义控件,该控件封装了将视频的Panel
或静态图像的PictureBox
带到前面,那也无法工作。但是,将一个PictureBox
和一个Panel
放在主窗体上,并调用您正在使用的那个控件的BringToFront
则可以正常工作。
同时播放两个视频?
我的下一个问题是阻止我前进的。我的应用程序运行在两个屏幕上,一个屏幕显示全屏视频,另一个屏幕显示缩略图。我能看到唯一的方法是播放两个视频。带有缩略图的屏幕还有一个自定义控件,用于显示可用媒体列表。一旦使用此控件,主视频屏幕就会冻结。经过一些实验,我发现无论两个视频对象的大小如何,当出现绘图消息时,较大的那个都会冻结。客户对此非常不满意……
包装类?
我原本打算提供一个包装类,将Video
对象变成一个行为理性的东西,这样人们就可以使用它,但后来我从别人那里听说,他们在使用320 kbps的MP3时遇到了问题,并且在音频方面遇到了和我视频方面一样的回调问题。我主要是在视频上进行了测试,因为从功能上看,它似乎是音频的超集。所以在交付前一周,我删掉了我的项目,用Windows Media Player控件替换了DX9。是的,代码不那么漂亮,但您猜怎么着?一旦我弄清楚它发送给我的消息(并且每次都发送,我必须说),我突然发现一切都第一次正常工作了。不仅如此,我还可以毫无问题地播放两个大视频。唯一的技巧是阻止播放器自动启动,这样它们就可以在播放之前加载各自的视频副本。除非您有非常特殊的原因要使用DirectX9,否则我完全建议您放弃它,并使用C#中的Media Player控件来播放音频和视频。SDK中的示例提供了您需要的一切来弄清楚这一点,但如果很多人要求教程,我会写的。
下一步做什么?
我接手这项工作时做的第一件事就是从亚马逊订购一本关于Managed DirectX 9的书。如果您想了解有关音视频播放的信息,请不要购买这本书。本文提供的信息比这本书多得多。书中解释的唯一一个我没有涵盖的信息是这个。DirectX 9的C#部分的AudioVideoPlayback
功能不是完整的,只是足以允许简单地播放视频文件。Managed DirectX中没有计划更新/改进/添加此功能。在我看来,说得够多了。我只是不确定他们为什么费心去做。