DirectShow设计
1.DirectShow技术简介
DirectShow是DirectX开发包中关于流媒体处理的一个开发包,这个开发包可以进行音频和视频的捕捉,使用它可以开发DVD应用程序和数字TV应用程序。DirectShow支持的媒体文件主要有WMA、MOV、MPG、AVI、MP3、WAV、WMV等,无论是读取媒体文件还是将捕捉的数据写入到媒体文件,DirectShow开发包都能提供很好的技术支持。
2.DirectShow技术结构
DirectShow技术利用标准组件来处理流媒体数据,这些组件称为过滤器。过滤器带有输入和输出针角,或二者兼而有之。在DirectShow技术中处于最核心位置的就是作为“过滤器”的可插入标准组件,它是执行特定任务的COM对象。过滤器又分为源过滤器(source filter)、变换过滤器(Rransform filter)和表现过滤器(Renderer filter)等。过滤器通过向文件读写、修改数据和显示数据到输出设备上来操作流媒体。为了完成整个任务,必须要将所有过滤器Filter连接起来,这3种过滤器组成了过滤器图表结构,如图1所示。
图1 过滤器图表结构图
从图5中可以看出,过滤器图表是各种过滤器的集合,它是通过过滤器的输入、输出针脚顺序连接而成的,这些过滤器的针脚通过协商来决定它们将支持何种形式的多媒体。由于DirectShow支持可重构的过滤器图表结构,所以使用相同的软件组件可以播放多种类型的媒体。开发人员可以通过定义自己的过滤器来扩展DirectShow对媒体的支持功能。
在过滤器图表结构中,源过滤器用来从数据源获取数据并将数据传送到过滤器图表中。数据源可以是摄像机、因特网、磁盘文件等。转换过滤器用来获取、处理和传送媒体数据,它包括分离视频和音频的分解变换过滤器(Splitter transform filter)、解压视频数据的视频转换过滤器(Video transform filter)、解压音频数据的音频转换过滤器(Audio transform filter)。表现过滤器用来在硬件上(如显卡和声卡)或者是任何可以接受媒体数据的地方(如磁盘文件)表现媒体数据,它包括用来显示图像的视频表现过滤器(Video renderer filter)、将音频数据送到声卡上去的音频表现过滤器(Audio renderer filter)。
在过滤器图表中,为了完成特定的任务,必须将所有需要的过滤器连接起来,因此前级过滤器的输出必定成为下级过滤器的输入。一个过滤器至少有一个输入针(Input pin),并将特定的输出送到输出针(Output pin),如图2所示,为一个过滤器连接图。
图2 过滤器连接图
应用程序不需要对过滤器图表中的各个过滤器进行单独的处理,因为在更高的层次上,DirectShow提供一个称为过滤图表管理器的部件(FGM)管理着这些过滤器的连接和流媒体数据在过滤器之间的流动。网站源代码FGM提供了一套COM接口,应用程序可以通过它来访问过滤器图表,控制流媒体或者接收过滤器事件。如果需要,它可以自动的插入一个合适的解码器,并将转换过滤器的输出针脚连接到表现过滤器。应用程序可以通过与过滤图表管理器的通信来控制过滤器图表的活动。程序开发人员只需要调用API函数来实现对流媒体的控制,如run方法启动流媒体在过滤器图表(Filter graph)中的流动,pause方法暂停流媒体的播放,stop方法停止播放流媒体等。
3.DirectShow技术开发准备
DirectShow组件在“C:\WINDOWS\system32”目录下的Quartz.dll动态库中,要使C#代码引用COM对象和接口,必须将COM类型库转换为.NET框架元数据,从而有效地创建一个可以从任何托管语言调用的托管包装。在转换过程中需要使用FrameWork SDK自带的TlbImp命令工具,该命令工具在“D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\TlbImp.exe”目录下(取决于Visual Studio 2005的安装路径)。该命令的使用方法为:
TlbImp C:\WINDOWS\system32\quartz.dll out:C:\WINDOWS\system32\quartzDriectShow.dll
在DOS命令下执行转换成功,如图3所示。
图3 装换Quartz.dll
转换完成后需在应用程序引用quartzDriectShow,引用quartzDriectShow.dll组件步骤如下所示
(1)在Visual Studio 2005开发环境中,选择菜单“项目”/“添加引用”命令,弹出“添加引用”对话框。
(2)选择“浏览”选项卡,通过浏览找到引用quartzDriectShow.dll所在的位置,并引用到程序中。如图4所示。
(3)引入using quartzDriectShow后,在程序中就可以开发相关多媒体程序了。
开发人员还可以通过编写自己的过滤器扩展DirectShow多媒体支持。下面是DirectShow组件的接口。
l IFilterGraph:过滤通道接口。
l IFilterGraph2:增强的IfilterGraph。
图4 引用quartzDriectShow组件
l IGraphBuilder:最为重用的COM接口,用于手动或者自动构造过滤通道Filter Graph Manager。
l IMediaControl:用来控制流媒体(如流的启动和停止暂停等)播放控制接口。
l IMediaEvent:播放事件接口,该接口在FilterGraph发生一些事件时用来创建事件的标志信息并传送给应用程序。
l IMediaEventEx:扩展播放事件窗口。
l IMediaPosition:播放的位置和速度控制接口(控制播放放置只能为设置时间控制方式)。
l IMediaSeeking:另一个播放的位置和播放速度控制接口,有较强的位置选择功能设置播放格式。常用的控制播放方式为TIME_FORMAT_MEDIA_TIME,单位100ns;TIME_FORMAT_ FRAME,按帧播放。
l IBasicAudio:声音控制接口。
l IBasic Video:图像控制接口(波特率、宽度、长度等信息)。
l IVideoWindow:显示窗口控制接口(有关播放窗口的一切控制,包括caption显示,窗口位置控制等)。
l ISampleGrabber:捕获图像接口(可用于抓图控制)。
l IVideoFrameStep:控制单帧播放的接口。
注意:使用DirectShow接口编程有3个步骤,初始化接口→利用接口中的控制函数使用控制操作→释放接口。
4.使用测试工具GraphEdit
测试工具(GraphEdit)所在安装路径为“D:\mssdk\bin\DXUtils\graphedt.exe”。通过该工具,可以很直观地看到Filter Graph的运行及处理流程,方便进行程序调试。
首先运行GraphEdit,执行菜单“File”/“Render Media File”命令,然后选择一个媒体文件;当Filter Graph构建成功后,单击工具栏的【运行】按钮,播放已选择的媒体文件,如图5所示。
示例
DirectShow技术设计多媒体播放器
本示例利用DirectShow技术来实现多媒体播放器。程序运行结果如图6所示网站源代码。
图5 GraphEdit测试工具的使用
图6 DirectShow技术设计多媒体播放器
程序开发步骤如下所示。
(1)创建一个项目,命名为27_03,设置默认窗体的Text属性为“多媒体播放器”。
(2)在窗体上添加6个Button控件,主要用来操作播放的媒体文件,如图6所示。
(3)在窗体上添加一个TrackBar控件,用来显示媒体文件播放的进度。
(4)在窗体上添加一个Timer控件,用来计算媒体当前的进度。
(5)程序代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using quartzDriectShow;
namespace _7_03
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//设置常量
const int WM_APP = 0x8000;
const int WM_GRAPHNOTIFY = WM_APP + 1;
const int EC_COMPLETE = 0x01;
const int WS_CHILD = 0x40000000;
const int WS_CLIPCHILDREN = 0x2000000;
FilgraphManager m_objFilterGraph = null;
IBasicAudio m_objBasicAudio = null;
IVideoWindow m_objVidwoWindow = null;
IMediaEvent m_objMediaEvent = null;
IMediaEventEx m_obnMediaEventEx = null;
IMediaPosition m_objMediaPosition = null;
IMediaControl m_objMediaControl = null;
//枚举
enum MediaStatus { None, Stopped, Paused, Running };
MediaStatus m_CurrentStatus = MediaStatus.None;
private void Clear()
{
if (m_objMediaControl != null)
m_objMediaControl.Stop();
if (m_obnMediaEventEx != null)
m_obnMediaEventEx.SetNotifyWindow(0, 0, 0);
if(m_objVidwoWindow!=null)
{
m_objVidwoWindow.Visible = 0;
m_objVidwoWindow.Owner = 0;
}
if (m_objBasicAudio != null) m_objBasicAudio = null;
if (m_objFilterGraph != null) m_objFilterGraph = null;
if (m_objMediaControl != null) m_objMediaControl = null;
if (m_objMediaEvent != null) m_objMediaEvent = null;
if (m_objMediaPosition != null) m_objMediaPosition = null;
if (m_objVidwoWindow != null) m_objVidwoWindow = null;
if (m_obnMediaEventEx != null) m_obnMediaEventEx = null;
this.Text = "多媒体播放器";
timer1.Enabled = False;
trackBar1.Value = 0;
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "选择播放文件|*.mpg;*.avi;*.mov;*.wma;*.wav;*.mp3|All Files|*.*";
if (DialogResult.OK == openFile.ShowDialog())
{
this.Clear(); //清理正在播放的文件
m_objFilterGraph = new FilgraphManager();
try
{
m_objFilterGraph.RenderFile(openFile.FileName);
m_objBasicAudio = m_objFilterGraph as IBasicAudio;//图像控制接口
m_objVidwoWindow = m_objFilterGraph as IVideoWindow; //声音控制接口
m_objVidwoWindow.Owner =(int) this.Handle;
//设置窗口类型
m_objVidwoWindow.WindowStyle = WS_CHILD | WS_CLIPCHILDREN;
//设置播放容器
m_objVidwoWindow.SetWindowPosition(this.ClientRectangle.Left, this.ClientRectangle.Top, this. ClientRectangle.Width, this.ClientRectangle.Height-60);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//当出错时,为音频文件,将视频对象清空
m_objVidwoWindow = null;
}
m_objMediaEvent = m_objFilterGraph as IMediaEvent; //播放事件接口
m_obnMediaEventEx = m_objFilterGraph as IMediaEventEx; //扩展播放事件接口
m_obnMediaEventEx.SetNotifyWindow((int)this.Handle, WM_GRAPHNOTIFY, 0);
m_objMediaPosition = m_objFilterGraph as IMediaPosition; //播放位置和速度控制
m_objMediaControl = m_objFilterGraph as IMediaControl; //用来控制流媒体播放控制接口
this.Text = openFile.FileName +"----"+ this.Text;
m_objMediaControl.Run(); //播放文件
try
{
trackBar1.LargeChange = 1;
trackBar1.Maximum = (int)m_objMediaPosition.Duration;
timer1.Enabled = True;
}
catch { }
}
}
private void button2_Click(object sender, EventArgs e)
{
//播放
if (m_objMediaControl!=null)
m_objMediaControl.Run();
}
private void button3_Click(object sender, EventArgs e)
{
//暂停
m_objMediaControl.Pause();
}
private void button4_Click(object sender, EventArgs e)
{
//停止本教程来自:http://www.isstudy.com
m_objMediaControl.Stop();
m_objMediaPosition.CurrentPosition = 0;
this.Clear();
}
private void button5_Click(object sender, EventArgs e)
{
//快退
if (m_objMediaPosition.CurrentPosition >= 1)
m_objMediaPosition.CurrentPosition = m_objMediaPosition.CurrentPosition - 1;
else
m_objMediaPosition.CurrentPosition = 0;
}
private void button6_Click(object sender, EventArgs e)
{
//快进
m_objMediaPosition.CurrentPosition = m_objMediaPosition.CurrentPosition + 1;
}
private void timer1_Tick(object sender, EventArgs e)
{
//显示文件播放的进度
trackBar1.Value = (int)m_objMediaPosition.CurrentPosition;
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
//根据滚动进度条,来播放文件
m_objMediaPosition.CurrentPosition = trackBar1.Value;
}
}
}
完整程序代码如下:
★ ★★★★Form1.cs窗体代码文件完整程序代码网站源代码★★★★★
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using quartzDriectShow;
namespace _7_03
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//设置常量
const int WM_APP = 0x8000;
const int WM_GRAPHNOTIFY = WM_APP + 1;
const int EC_COMPLETE = 0x01;
const int WS_CHILD = 0x40000000;
const int WS_CLIPCHILDREN = 0x2000000;
FilgraphManager m_objFilterGraph = null;
IBasicAudio m_objBasicAudio = null;
IVideoWindow m_objVidwoWindow = null;
IMediaEvent m_objMediaEvent = null;
IMediaEventEx m_obnMediaEventEx = null;
IMediaPosition m_objMediaPosition = null;
IMediaControl m_objMediaControl = null;
enum MediaStatus { None, Stopped, Paused, Running };
MediaStatus m_CurrentStatus = MediaStatus.None;
private void Form1_Load(object sender, EventArgs e)
{
}
private void Clear()
{
if (m_objMediaControl != null)
m_objMediaControl.Stop();
if (m_obnMediaEventEx != null)
m_obnMediaEventEx.SetNotifyWindow(0, 0, 0);
if(m_objVidwoWindow!=null)
{
m_objVidwoWindow.Visible = 0;
m_objVidwoWindow.Owner = 0;
}
if (m_objBasicAudio != null) m_objBasicAudio = null;
if (m_objFilterGraph != null) m_objFilterGraph = null;
if (m_objMediaControl != null) m_objMediaControl = null;
if (m_objMediaEvent != null) m_objMediaEvent = null;
if (m_objMediaPosition != null) m_objMediaPosition = null;
if (m_objVidwoWindow != null) m_objVidwoWindow = null;
if (m_obnMediaEventEx != null) m_obnMediaEventEx = null;
this.Text = "多媒体播放器";
timer1.Enabled = false;
trackBar1.Value = 0;
}
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.Filter = "选择播放文件|*.mpg;*.avi;*.mov;*.wma;*.wav;*.mp3|All Files|*.*";
if (DialogResult.OK == openFile.ShowDialog())
{//本教程来自:http://www.isstudy.com
this.Clear(); //清理正在播放的文件
m_objFilterGraph = new FilgraphManager();
try
{
m_objFilterGraph.RenderFile(openFile.FileName);
m_objBasicAudio = m_objFilterGraph as IBasicAudio;//图象控制接口
m_objVidwoWindow = m_objFilterGraph as IVideoWindow; //声音控制接口
m_objVidwoWindow.Owner =(int) this.Handle;
//设置窗口类型
m_objVidwoWindow.WindowStyle = WS_CHILD | WS_CLIPCHILDREN;
//设置播放容器
m_objVidwoWindow.SetWindowPosition(this.ClientRectangle.Left, this.ClientRectangle.Top, this.ClientRectangle.Width, this.ClientRectangle.Height-60);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//当出错时,为音频文件,将视频对象清空
m_objVidwoWindow = null;
}
m_objMediaEvent = m_objFilterGraph as IMediaEvent; //播放事件接口
m_obnMediaEventEx = m_objFilterGraph as IMediaEventEx; //扩展播放事件接口
m_obnMediaEventEx.SetNotifyWindow((int)this.Handle, WM_GRAPHNOTIFY, 0);
m_objMediaPosition = m_objFilterGraph as IMediaPosition; //播放位置和速度控制
m_objMediaControl = m_objFilterGraph as IMediaControl; //用来控制流媒体播放控制接口
this.Text = openFile.FileName +"----"+ this.Text;
m_objMediaControl.Run(); //播放文件
try
{
trackBar1.LargeChange = 1;
trackBar1.Maximum = (int)m_objMediaPosition.Duration;
timer1.Enabled = true;
}
catch { }
}
}
private void button2_Click(object sender, EventArgs e)
{
//播放
if (m_objMediaControl!=null)
m_objMediaControl.Run();
}
private void button3_Click(object sender, EventArgs e)
{
//暂停
m_objMediaControl.Pause();
}
private void button4_Click(object sender, EventArgs e)
{
//停止
m_objMediaControl.Stop();
m_objMediaPosition.CurrentPosition = 0;
this.Clear();
}
private void button5_Click(object sender, EventArgs e)
{
//快退
if (m_objMediaPosition.CurrentPosition >= 1)
m_objMediaPosition.CurrentPosition = m_objMediaPosition.CurrentPosition - 1;
else
m_objMediaPosition.CurrentPosition = 0;
}
private void button6_Click(object sender, EventArgs e)
{
//快进
m_objMediaPosition.CurrentPosition = m_objMediaPosition.CurrentPosition + 1;
}
private void timer1_Tick(object sender, EventArgs e)
{
//显示文件播放的进度
trackBar1.Value = (int)m_objMediaPosition.CurrentPosition;
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
//根据滚动进度条,来播放文件
m_objMediaPosition.CurrentPosition = trackBar1.Value;
}
}
}
★ ★★★★Form1.Designer.cs窗体设计文件完整程序代码★★★★★
namespace _7_03
{
partial class Form1
{
///
/// 必需的设计器变量。
///
private System.ComponentModel.IContainer components = null;
///
/// 清理所有正在使用的资源。
///
/// 如果应释放托管资源,为 true;否则为 false。
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
///
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
///
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
this.button6 = new System.Windows.Forms.Button();
this.trackBar1 = new System.Windows.Forms.TrackBar();
this.timer1 = new System.Windows.Forms.Timer(this.components);
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(7, 254);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 1;
this.button1.Text = "打开";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(88, 254);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(75, 23);
this.button2.TabIndex = 2;
this.button2.Text = "播放";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// button3
//本教程来自:http://www.isstudy.com
this.button3.Location = new System.Drawing.Point(331, 254);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(75, 23);
this.button3.TabIndex = 3;
this.button3.Text = "暂停";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(412, 254);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(75, 23);
this.button4.TabIndex = 4;
this.button4.Text = "停止";
this.button4.UseVisualStyleBackColor = true;
this.button4.Click += new System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(169, 254);
this.button5.Name = "button5";
this.button5.Size = new System.Drawing.Size(75, 23);
this.button5.TabIndex = 5;
this.button5.Text = "<<快退";
this.button5.UseVisualStyleBackColor = true;
this.button5.Click += new System.EventHandler(this.button5_Click);
//
// button6
//
this.button6.Location = new System.Drawing.Point(250, 254);
this.button6.Name = "button6";
this.button6.Size = new System.Drawing.Size(75, 23);
this.button6.TabIndex = 5;
this.button6.Text = ">>快进";
this.button6.UseVisualStyleBackColor = true;
this.button6.Click += new System.EventHandler(this.button6_Click);
//
// trackBar1
//
this.trackBar1.Cursor = System.Windows.Forms.Cursors.NoMoveHoriz;
this.trackBar1.LargeChange = 1;
this.trackBar1.Location = new System.Drawing.Point(1, 210);
this.trackBar1.Maximum = 50;
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(494, 45);
this.trackBar1.TabIndex = 6;
this.trackBar1.TickStyle = System.Windows.Forms.TickStyle.Both;
this.trackBar1.Value = 1;
this.trackBar1.Scroll += new System.EventHandler(this.trackBar1_Scroll);
//本教程来自:http://www.isstudy.com
// timer1
//
this.timer1.Interval = 500;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(496, 280);
this.Controls.Add(this.button6);
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Controls.Add(this.trackBar1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "Form1";
this.Text = "多媒体播放器";
this.Load += new System.EventHandler(this.Form1_Load);
((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button6;
private System.Windows.Forms.TrackBar trackBar1;
private System.Windows.Forms.Timer timer1;
}
}
★ ★★★★Program.cs主程序文件完整程序代码★★★★★
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace _7_03
{
static class Program
{
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}