DirectShow实践经验杂谈

转载 2006年06月15日 17:07:00

关键字 DirectShow
原作者姓名 陆其明
文章原始出处 http://hqtech.nease.net

读者评分 3 评分次数 1

正文
1.当向Filter Graph中加入同一个Filter的多个实例时,使用Intelligent connect,优先使用最晚加入Filter Graph中的那个Filter实例。

2.使用IGraphConfig接口可以将Filter加入Cache,以在Intelligent connect时,提高该Filter的连接优先级。如果要加入Cache的Filter已在Graph中,确信它的所有Pin处于断开状态,而且调用IGraphConfig::AddFilterToCache之后,Graph中的Filter实例会自动Remove掉;当Intelligent connect之后,使用了Cache中的某个Filter实例,则这个Filter实例会被自动加入到Graph,而Cache中这个Filter实例也会被Remove掉。

3.在调试Filter时,每次进入CheckMediaType的Media type,我们在VC中只能看到一串UUID数字,很麻烦!下面的代码能将Media type的描述信息Dump到VC的Output window:
void DisplayMediaType(TCHAR *pDescription,const CMediaType *pmt)
{
// Dump the GUID types and a short description
DbgLog((LOG_TRACE,0,TEXT("")));
DbgLog((LOG_TRACE,0,TEXT("%s"),pDescription));
DbgLog((LOG_TRACE,0,TEXT("")));
DbgLog((LOG_TRACE,0,TEXT("Media Type Description")));
DbgLog((LOG_TRACE,0,TEXT("Major type: %s"),GuidNames[*pmt->Type()]));
DbgLog((LOG_TRACE,0,TEXT("Subtype: %s"),GuidNames[*pmt->Subtype()]));
DbgLog((LOG_TRACE,0,TEXT("Subtype description: %s"),GetSubtypeName(pmt->Subtype())));
DbgLog((LOG_TRACE,0,TEXT("Format size: %d"),pmt->cbFormat));
// Dump the generic media types
DbgLog((LOG_TRACE,0,TEXT("Fixed size sample %d"),pmt->IsFixedSize()));
DbgLog((LOG_TRACE,0,TEXT("Temporal compression %d"),pmt->IsTemporalCompressed()));
DbgLog((LOG_TRACE,0,TEXT("Sample size %d"),pmt->GetSampleSize()));
}

4.调试Filter时,想要知道程序是否进入某个函数,可以使用如下的宏定义:
#define DbgFunc(a) DbgLog(( LOG_TRACE                        /
                          , 0                                /
                          , TEXT("CFltTracer(Instance %d)::%s") /
                          , mThisInstance                  /
                          , TEXT(a)                          /
                         ));
使用方法为:
CFltTracer::~CFltTracer()
{
    // Other cleaning work...
    DbgFunc("~CFltTracer");
}

5.如下的代码,可以将DirectShow的错误码以文本的方式显示出来:
void ShowError(HRESULT hr)
{
    if (FAILED(hr))
    {
        TCHAR szErr[MAX_ERROR_TEXT_LEN];
        DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
        if (res == 0)
        {
            wsprintf(szErr, "Unknown Error: 0x%2x", hr);
        }
        MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
    }
}

6.0进行DV camcorder编程时要注意,当Filter Graph在运行,而将Camcorder置于Paused状态,"Microsoft DV Camera and VCR"仍然会不停地将Paused时刻的那一帧不断地发送出来(因为DirectShow主要是为Playback设计的,所以这一点对于压缩、合成、写文件的应用非常头疼),送出的Sample时间戳线性递增;而将Camcorder置于Stopped状态,"Microsoft DV Camera and VCR"也就不再送出数据。而Filter Graph上的Pause、Stop操作并不会影响到Camcorder本身的状态。

6.1 Camcorder机器Paused状态持续大约三分钟(测试机为JVC)后,机器的Preview画面会变成蓝屏。此时,即使Filter Graph在运行状态,"Microsoft DV Camera and VCR"也停止输出数据。

7.DirectShow加入Device Filter一般都要靠枚举。Device的名字一般是在枚举的时候,通过IPropertyBag::Read(L"FriendlyName", &varName, 0)获得。对于DV camcorder,这种方式得到的名字为"Microsoft DV Camera and VCR";而另一种方法(必须在Windows Me或XP下),通过读取"Description"属性,可以更详细地得到Camcorder的生产厂商名字。参考代码如下:
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"Description", &varName, 0);
if (FAILED(hr))
{
    hr = pPropBag->Read(L"FriendlyName", &varName, 0);
}  

8.Playback快进(慢进)的支持,可以通过IMediaSeeking::SetRate(或IMediaPosition::put_Rate)来实现。参数为1.0表示正常速度,2.0为双倍速度,0.5为半速;理论上参数为负数时能够实现倒放,但绝大部分Filter不支持倒放。对于Rate改变的响应,主要工作在负责打时间戳的Filter上,有可能是Parser Filter(如AVI Splitter),也有可能是Push模式下的Source Filter;这些Filter需要根据新的Rate重新打好时间戳。一般Decoder Filter不用对Rate改变做出响应,它们收到NewSegment后只要往下传递就行了,但对于一些可能需要改变时间戳的Decoder,则也需要考虑这个Rate的改变。Video Renderer一般也不需要关心Rate的改变,因为进来的Sample已经是按照新的Rate打好时间戳了;Audio Renderer需要对Rate进行更新,因为一般Audio Decoder不会因为Rate的改变而作相应的转换。还有一点,SetRate的调用之前,Filter Graph Manager会先将Filter Graph停止;调用之后,新的Sample时间戳从0开始。Playback Rate的改变会导致Audio很难听,所以此时最好将Audio静音处理。

9.Playback的单帧控制,可以通过Filter Graph Manager上获得的IVideoFrameStep接口来实现。Filter Graph Manager也主要是协同Video Renderer或者Overlay Mixer来实现单帧播放的。IVideoFrameStep接口方法不仅支持单帧跳进,也支持多帧的跳进。在使用接口方法时,最好先调用IVideoFrameStep::CanStep判断是否支持你指定的跳帧操作。调用IVideoFrameStep::Step实现跳进,其中第二个参数为实际实现跳帧功能的Filter,为NULL时Filter Graph Manager默认使用Video Renderer。当Step函数调用成功,Filter Graph Manager会向应用程序发送一个EC_STEP_COMPLETE事件,Filter Graph也自动转入Paused状态。

10.Mpeg1文件的播放可以直接使用微软的MPEG-1 Stream Splitter、MPEG Video Decoder和MPEG Audio Decoder。Video Decoder可以通过IMpegVideoDecoder接口(在mpgcodec.h中定义)进行参数修改,比如可以通过set_GreyScaleOutput设置输出图像是彩色的还是黑白的等;Audio Decoder可以通过IMpegAudioDecoder接口(在mpegtype.h中定义)进行参数设置,比如可以控制解码输出mono或是stereo,左右声道切换(注意:在解码输出mono的时候,不支持动态切换左右声道)等。
11.AVI Splitter, Mpeg1 Stream splitter, Mpeg2 splitter只能工作在Pull模式;Mpeg2 Demultiplexer在非Window XP下工作在Push模式,而在Window XP下也能工作在Pull模式。所以,网络客户端接收压缩数据要进行解码时,一般将接收器Source Filter写成Pull模式(因为现成的Parser Filter大都只能工作在Pull模式)。如果要将接收器写成Push模式,则这个Source Filter送出来的数据应该是已经做过Parse处理的(即Video和Audio已分离)。

12.ACM(Audio Compression Manager)和VCM(Video Compression Manager)在DirectShow中都是通过包装Filter应用的。ACM Wrapper Filter,作为解压用时(主要进行Audio格式的转换,输出PCM数据),注册在"DirectShow Filters"目录下,Merit值为MERIT_NORMAL;作为压缩用时,各个Audio压缩器注册在"Audio Compressors"目录(CLSID_AudioCompressorCategory)下,Merit值为MERIT_DO_NOT_USE。注意:这里的Audio Compressor不能通过CoCreateInstance创建,而只能通过系统枚举。VCM包装Filter作为解压用时,即为AVI Decompressor Filter,一般实现Video从YUV到RGB格式的转换(注意:MPEG数据的压缩/解码都不是通过VCM包装Filter实现的);作为压缩用时,各压缩器注册在"Video Compressors"目录(CLSID_VideoCompressorCategory)下,Merit值为MERIT_DO_NOT_USE。注意:这里的Video Compressor也不能直接通过CoCreateInstance创建。

13.微软提供了两个Tee Filter:Smart Tee和Infinite Pin Tee Filter。前者有两个Output pin,且Preview pin输出的Sample已经去掉时间戳;后者,可以动态产生无数个Output pin,而且各个Output pin输出的Sample是完全一样的。

14.在Filtr Graph运行状态下,可以动态加入新的Filter,但不能删掉Filter,也不能断开Filter之间的Pin连接。Filter从Filter Graph中删除必须在Stopped状态,删除前将这个Filter的Pin断开不是必要的!(连接着的Pin可以通过IFilterGraph::Disconnect断开,并且连接两头的Pin都要调用一次!)

15.AVI Mux问题。四种工作模式(通过Filter上的IConfigInterleaving::put_Mode设置):INTERLEAVE_NONE,对输入的数据不缓存,各帧数据按照它们到达Mux的顺序直接写入文件中,速度最快;INTERLEAVE_FULL,对Video和Audio的交叉打包精确控制,每次合成都要阻塞等待等量的Video、Audio数据,适合文件源的视频编辑等应用;INTERLEAVE_CAPTURE,处于前两种模式之间,尽量不使用数据缓存,在一个Pin上数据到达后等待另一个Pin数据到达时,可能会丢帧,适合Live Source Capture应用,注意此时Audio的Capture buffer应该小于0.5秒;INTERLEAVE_NONE_BUFFERED,仅在Win XP下用,类似于INTERLEAVE_NONE,只是这种模式生成的文件较小。还有另外一个接口方法IConfigInterleaving::put_Interleaving,设置打包频率和Audio的预留(Preroll),推荐打包频率为1秒钟一次,Audio预留750ms。

16.AVI 1.0文件大小有上限1GB,AVI 2.0文件没有这个限制。AVI Mux默认总是生成2.0的文件(1.0的文件主要是老的VFW生成的),也可以通过AVI Mux上的IConfigAviMux::SetOutputCompatibilityIndex来设置生成1.0的文件,以保持向后兼容。另一个接口方法IConfigAviMux::SetMasterStream,用以同步多个输入流,特别是在不同源的video、Audio Capture时,两个源的Capture rate可能有细微的差别。设置了Master stream之后,Mux会修改AVI文件的Playback rates(AVIStreamHeader结构的dwScale和dwRate两个成员变量)。推荐将Audio stream设为Master。

17.在同一个Filter Graph中,既有Live Source(比如Camcorder,capture devices,mpeg2 demux等,他们自带Reference Clock),又有Default DirectSound Device(声卡带有时钟),默认情况下,Filter Graph Manager选择Live Source的参考时钟。有时候会出现Live Source数据流传输了几十个Sample后会不在传送Sample出来。可能的原因是,有多个参考时钟时回放速率匹配问题。解决的方法是,选择声卡的时钟,或者设置整个Filter Graph不使用时钟。

18.0目前(DirectX8.1),微软并没有提供.dv文件的“拉”Filter。如果要使用DV文件,一个变通的方法是,将DV数据保存到AVI文件中。播放这种文件,AVI Splitter会识别出DV数据,并送DV Splitter(微软提供,Push模式)和DV Video Decoder(微软提供)进行解码。

18.1在回放含有DV的AVI文件时,我们发现,真正实现IMediaSeeking的是AVI Splitter,而不是DV Splitter(微软提供)。DV Splitter的Audio output pin或者Video Output pin均将IMediaSeeking的请求通过其Input pin传递到Up stream去了。

18.2如果自己写一个"DV Parser Filter",需要注意的是,从Filter Source中拉数据,每次拉的数据大小一般是按512字节对齐的,所以不会是每次拉一个DV帧(PAL为144000字节,NTSC为120000字节)。而微软的DV Splitter输入的Sample要求是一帧一帧的数据(注意:如果每次送的数据太多了,会导致无法正常解码;如果送少了,导致后续Filter没有足够的数据进行解码,会发生Filter Graph的状态转换出错,即可能无法转入Paused状态),所以我们必须在DV Parser上对拉出的数据进行必要的缓存。而且,在处理Seek时,新的Seeking位置也要考虑“字节对齐”的问题。
其实,为DV文件写一个Source Filter,直接将DV数据从文件中读出后push出去,问题就要简单一点!

19.Tee Filter问题。微软提供的Smart Tee(没有源码)与Infinite Pin Tee Filter(有源码)相比,后者的性能要好一点。两者的区别是,前者将Preview pin出来的Sample进行了“去时间戳”处理,而后者只是简单地将一个Sample分别在各个output pin上输出。

20.写多进一出,或者是一进多出的Filter,基类可以选择CTransformFilter或者CTransInPlaceFilter,虽然它们在BaseClasses中的实现都是一进一出的。一般的做法为,保持原有的Input pin和Output pin为“主流”的链路,以这个标准Input pin的数据“流入”,来驱动协调其他Pin数据处理后,再输入。需要注意的是,必须重载基类Filter的GetPinCount、GetPin实现,以增加新的Pin;重载FindPin支持新加pin;重载Filter的Pause或Stop实现,因为可能还要同步新增加的pin的数据流(如果新增加的pin在Filter要转入Paused状态时还处于死循环或阻塞,则可能导致Filter状态转换失败,出现Frozen现象);新增加Input pin一般从CBaseInputPin上继承,Output pin从CBaseOutputPin上继承,而且一般都要重载以下几个函数的实现:CheckMediaType、Receive、EndOfStream、BeginFlush、EndFlush。

相关文章推荐

linux与防火墙实践经验

  • 2015年08月25日 10:40
  • 137KB
  • 下载

qtp学习与实践经验总结5

  • 2009年03月12日 15:49
  • 604KB
  • 下载

Socket 网络编程实践经验

目录 目录 前文 需要考虑的坑 前文 Python Module_Socket_网络编程 需要考虑的坑 网络断开重连问题 连接会话和身份认证问题 同步和异步问题 数据缓...
  • Jmilk
  • Jmilk
  • 2017年11月04日 13:27
  • 109

QTP学习与实践经验总结

  • 2013年06月04日 17:52
  • 5.43MB
  • 下载

同行代码评审过程中的实践经验

首先,让我们谨记为什么要做代码评审。对于任何专业的软件开发人员来说,最重要的目标之一是能够持续的提高他们的工作质量。即使你的团队里尽是优秀的程序员,你也不能将你自己与一个有能力的自由从业者区分开来,除...

软件项目量化管理(CMMI高成熟度)实践经验谈——之项目管理过程策划篇

续:软件项目量化管理(CMMI高成熟度)实践经验谈——之概述篇
  • xiaoyw
  • xiaoyw
  • 2014年05月17日 17:13
  • 2685

2010的应届生谈SEO实践经验

我是一位2010的应届生,虽然现在已经不是了。不过我还是很怀念刚来到公司的时候那种激情,今天突然心血来潮,想写一点东西,却不知从何写起,我的学历不高,头脑一般,办事效率还行,上头吩咐下来的工作总是拼了...

UIScrollView 实践经验

UIScrollView(包括它的子类 UITableView 和 UICollectionView)是 iOS 开发中最常用也是最有意思的 UI 组件,大部分 App 的核心界面都是基于三者之一...

ArcGI Server+GP服务完成最短路径分析(实践经验)

最近用arcgis GP服务做了几个Demo,为了不丢失自己的实践经验,我决定把过程记下来!!! (一)最短路径分析 以上为效果图,在此感谢 http://www.t...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:DirectShow实践经验杂谈
举报原因:
原因补充:

(最多只允许输入30个字)