65.9K
CodeProject 正在变化。 阅读更多。
Home

在 C# 视频应用程序中使用第三方过滤器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.14/5 (3投票s)

2006年1月4日

5分钟阅读

viewsIcon

146755

downloadIcon

2099

一个用于测试 DirectShow 滤波器的简单应用程序。

引言

DirectShow 过滤器是 Windows 平台上多媒体应用程序的基本构建块。其中许多过滤器由微软作为其操作系统的一部分提供。但更多的过滤器由 ISV 和开发人员创建。在本文中,我们将学习如何在简单的视频应用程序中使用我们之前编写的过滤器。

背景

上一篇文章中,我用 C# 创建了两个过滤器。在本文中,我将创建一个简单应用程序,该应用程序可以利用这些过滤器。该应用程序将有两个窗口:一个显示原始视频,另一个显示将过滤器应用于视频后的结果。此外,该应用程序还包含用于控制视频和 Sobel 变换阈值的控件。

除了测试过滤器,我们还将利用微软的“Infinite Tee”过滤器来复制视频流,以供我们的两个窗口使用。为该应用程序创建的图形并非完全简单,我们将有机会编写几个方法来简化此任务。

使用代码

要运行该应用程序,必须在您的计算机上安装黑白过滤器和 Sobel 变换过滤器。更多信息可以在我之前的文章中找到。要编写使用这些过滤器或任何其他第三方过滤器的程序,您需要拥有过滤器的 GUID。它们应该由供应商提供;在我们的例子中,我们拥有过滤器的源代码,并且我们发现我们正在寻找的 GUID 是

// the Guids for our custom filters
Guid sobelGuid = new Guid( "1C826B9A-4008-4C41-B601-A783A40AFAB2" );
Guid bwGuid = new Guid( "0C017086-684E-41c9-A4BB-640570C64B28" );

此外,我们的 Sobel 过滤器提供了一个自定义 COM 接口,因此我们的代码具有以下声明

// 
// The ISobel interface is a custom COM interface
// exposed by the Sobel filter, in order to access it
// from C#, we need to define it
//
[Guid("263AFD9E-465C-4dde-9BB1-168C25E2B87F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ISobel
{
    int SetThreshold( int newValue );
}

关注点

除了几个通用的 DirectShow 接口,我们还使用以下对象

// We'll use an Infinite Tee filter object to duplicate the
// video stream by two
InfTee tee = null;

// We'll only treat wmv format 
WMAsfReader reader =  null;

// The Sobel object and filter interface
object sobelObject = null;
IBaseFilter sobel = null;
// the black and white filter interface
IBaseFilter bw = null;

Infinite Tee 过滤器是一个微软提供的过滤器,它将接收到的样本传递给任意数量的输出引脚。我们用它来显示一个样本(或者在本例中,一个视频帧)而不进行任何转换,并将帧的副本馈送到我们的黑白过滤器。

我们想要构建的图形如下所示

我们必须将 Black and White、Infinite Tee 和 Sobel 过滤器的对象添加到过滤器图中。这些步骤没有什么不寻常的。然后,我们配置一个 `WMAsfReader` 对象(如果您想处理 Windows Media 文件以外的其他文件类型,您需要编写一些额外的代码)。现在,我们必须连接 Infinite Tee 过滤器的引脚。为此,我们有以下代码

// connect video source to infinite tee filter
IPin teeInput = DsFindPin.ByDirection( (IBaseFilter)tee, 
                                PinDirection.Input, 0 );
IPin vidOutput = DsFindPin.ByDirection( (IBaseFilter)reader, 
                                   PinDirection.Output, 0 );
// check if the video stream is on the first pin
if( CheckVideo( vidOutput ) )
  graphBuilder.Connect( vidOutput, teeInput );
else // video stream is on second pin
{
  vidOutput = DsFindPin.ByDirection( (IBaseFilter)reader, PinDirection.Output, 1 );
  graphBuilder.Connect( vidOutput, teeInput );
}

首先,我们获取 Infinite Tee 过滤器的输入引脚和 `WMAsfReader` 的输出引脚,并检查 `WMAsfReader` 的第一个输出引脚是否是视频流。如果是,我们将其与 Infinite Tee 输入引脚连接。如果不是视频流,我们假设第二个引脚是视频流,并将其连接到 Infinite Tee 过滤器。由于 Infinite Tee 过滤器的第一个输出引脚现在是视频流,我们调用 `Render`(未显示)来完成图形的这一部分。因为 Black and White 过滤器和 Sobel 过滤器在此调用之前已添加到图中,“Intelligent Connect”将在它将添加以渲染此引脚的视频渲染器之前插入它们。然后,我们配置第一个视频渲染窗口,特别是,我们将它的 `owner` 属性设置为我们窗体使用的面板之一。我们预计当我们在图中搜索第二个视频渲染器时,这个条件稍后会得到满足。

然后我们调用 Infinite Tee 过滤器的第二个输出引脚的 `Render`。由于我们的自定义过滤器已在第一次调用 `Render` 中使用,此调用将创建一个“标准”路径到新的视频渲染器对象。现在,我们面临一个问题:我们在图中有了两个视频渲染器,并且我们想配置第二个,我们该怎么做?好吧,`GetSecondRenderer` 方法是为了简化这个任务而编写的

//
// This method find the second renderer in the filter graph;
// it assumes that the first renderer had its "owner" property set
//
IVideoWindow GetSecondRenderer() 
{
  IEnumFilters enumFilters;
  ArrayList filtersArray = new ArrayList();

  IFilterGraph filterGraph = (IFilterGraph)fg;
  filterGraph.EnumFilters(out enumFilters);
  
  IBaseFilter[] filters = new IBaseFilter[1];
  int fetched;

  while(enumFilters.Next(1, filters, out fetched) == 0)
  {
    IVideoWindow ivw = filters[0] as IVideoWindow;
    if( ivw != null ) 
    {
      IntPtr outPtr = new IntPtr();
      ivw.get_Owner( out outPtr );
      if( outPtr == IntPtr.Zero )
        return ivw;
    }
  }
  return null;
}

我们枚举图中的所有过滤器,并查询 `IVideoWindow` 接口。如果成功,那么我们检查“`owner`”属性。如果它是“null”,我们就知道我们找到了正确的渲染器。之后,我们设置一些属性,就可以运行图形了。

一个有趣的处理程序是 trackbar `ValueChanged` 事件的处理程序。我们想改变 Sobel 变换过滤器的阈值。但这个属性是 Sobel 过滤器对象上的自定义 COM 接口,所以我们使用以下代码

// access the ISobel interface on the sobel object
ISobel isobel = sobelObject as ISobel;
if( isobel != null ) 
{
  isobel.SetThreshold( trackBar1.Value );
  label1.Text = "Treshold value: " + trackBar1.Value.ToString();
}

我们查询 COM 组件对象的 `ISobel` 接口,并在此接口上调用 `SetThreshold` 方法。

局限性和已知问题

Black and White 过滤器非常小巧,它的处理对于常见的“320X240”视频文件有效,但对于其他设置可能会遇到困难。

原始帧和转换后的帧之间存在很小的延迟。由于 Sobel 变换计算量很大,这是可以预期的,但我没有测量“Infinite Tee”的开销(如果有的话)。

该应用程序只处理 Windows Media 文件,很容易修改它以处理例如 `.mpeg` 和 `.avi` 文件。

© . All rights reserved.