zoukankan      html  css  js  c++  java
  • [转]DirectShow应用——视频捕捉WDM Vs VFW

    本文转自:http://bbs.njupt.edu.cn/cgi-bin/bbsanc?path=/groups/computer.faq/MultiMedia/D52F2B929/D99113C57/X70CFBCB7

    发信人: yellowdawn (追逐天边的云), 信区: MultiMedia
    标  题: DirectShow应用——视频捕捉WDM Vs VFW
    发信站: 紫金飞鸿 (2005年01月26日18:32:09 星期三), 站内信件

    作者:陆其明
    关键字 DirectShow Live Capture WDM VFW

    说起视频捕捉问题,我们先要来看一下视频捕捉卡。根据使用的驱动程序的不同来分类
    ,目前市场上大致有两种捕捉卡:VFW (Video for Windows)卡和WDM (Windows Driver
    Model)卡。前者是一种趋于废弃的驱动模型,而后者是前者的替代模型;WDM还支持更多
    新的特性,比如直接支持电视接收、视频会议、1394接口的设备、桌面摄像机、多条视
    频流(Line-21或Closed-Caption等)同时输出等等。采用VFW的一般都是些以前生产的
    卡;市面上新出现的,一般都是采用了WDM驱动程序。另外,视频捕捉卡的接口,可以是
    以PCI或AGP的方式插入PC机箱,也可以直接以USB接口的方式外挂;还有就是通过1394接
    口与PC机相连的数码摄像机等等。

    使用DirectShow来处理一般的视频捕捉问题,是相对比较简单的。这当然得益于DirectS
    how这一整套先进的应用架构。捕捉卡通常也是以一个(Capture) Filter的形式出现的。
    处理视频捕捉,我们同样是使用Filter Graph,同样是操作Filter;控制起来,就似于
    操作媒体文件的播放。当然,这主要是从应用程序控制层面上来说的;视频捕捉的应用
    场合比较多,视频捕捉本身的一些处理还是有它的特殊性的,而且牵涉面比较广。本文
    侧重于阐述一个建立视频捕捉程序的一般过程,以及WDM与VFW的兼容性问题。

    当视频捕捉卡正确安装到系统中后,使用GraphEdit插入Filter,我们可以在“Video
    Capture Sources”目录下看到代表捕捉卡的那个Filter。一般一个Capture Filter至少
    有一个Capture Output Pin;典型的情况下,还有一个Preview Pin或者Video Port
    Pin(一般Preview Pin和VP Pin不会共存)。有些视频捕捉卡,能够同时捕捉Video和Au
    dio,那么它的Filter自然还应该有Audio部分的输出Pin。视频捕捉卡都注册在CLSID_Vi
    deoInputDeviceCategory目录之下;要知道系统中安装了哪些捕捉卡,需要利用系统枚
    举,如下:
    ICreateDevEnum *pDevEnum = NULL;
    IEnumMoniker *pEnum = NULL;
    // Create the System Device Enumerator.
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
    CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
    reinterpret_cast<void**>(&pDevEnum));
    if (SUCCEEDED(hr))
    {
    // Create an enumerator for the video capture category.
    hr = pDevEnum->CreateClassEnumerator(
    CLSID_VideoInputDeviceCategory, &pEnum, 0);
    }
    Capture Filter的创建也不是象其他Filter一样使用CoCreateInstance,而是在枚举的
    过程中BindToObject。在这一点上,对WDM卡和VFW卡的处理是一致的。

    将Capture Filter加入Filter Graph之后,剩下的Filter怎么连接?DirectShow给我们
    提供了一个简单的解决方法:使用ICaptureGraphBuilder2接口。如下创建:
    IGraphBuilder *pGraph = 0;
    ICaptureGraphBuilder2 *pBuild = 0;
    // Create the Capture Graph Builder.
    HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 0,
    CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
    (void**)&pGraph);
    if (SUCCEEDED(hr))
    {
    // Create the Filter Graph Manager.
    hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void**)&pGraph);
    if (SUCCEEDED(hr))
    {
    // Initialize the Capture Graph Builder.
    pBuild->SetFiltergraph(pGraph);
    }
    }
    接下来,就是使用ICaptureGraphBuilder2::RenderStream来继续各个Output Pin的连接
    。值得注意的是,这里有一个Pin Category的概念,作为RenderStream的第一个参数,
    比如Preview Pin的目录为PIN_CATEGORY_PREVIEW,Capture Pin的目录为PIN_CATEGORY_
    CAPTURE等等。下面是Preview Pin的连接示例:
    ICaptureGraphBuilder2 *pBuild; // Capture Graph Builder
    // Initialize pBuild (not shown).
    IBaseFilter *pCap; // Video capture filter.
    /* Initialize pCap and add it to the filter graph (not shown). */
    hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
    pCap, NULL, NULL);
    调用RenderStream实现Preview链路,不管Capture Filter是否有Preview Pin或者只有V
    P Pin,Capture Graph Builder都能自动正确地处理。(如果只有VP Pin,则自动连接V
    P Pin;如果Capture Filter只有一个Capture Output Pin,则自动插入一个Smart Tee
    Filter然后再连接。)

    要实现视频捕捉到文件,最简单的方法也是使用ICaptureGraphBuilder2::RenderStream
    。如下(假设生成的是AVI文件):
    IBaseFilter *pMux;
    hr = pBuild->SetOutputFileName(
    &MEDIASUBTYPE_Avi, // Specifies AVI for the target file.
    L"C:\\Example.avi", // File name.
    &pMux, // Receives a pointer to the mux.
    NULL);
    hr = pBuild->RenderStream(
    &PIN_CATEGORY_CAPTURE, // Pin category.
    &MEDIATYPE_Video, // Media type.
    pCap, // Capture filter.
    NULL, // Intermediate filter (optional).
    pMux); // Mux or file sink filter.

    // Release the mux filter.
    pMux->Release();
    下面是典型的两个经过RenderStream以后构建的Capture Filter Graph的示意图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=WDMCapture1.jpg

    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=WDMCapture2.jpg

    使用Capture Graph Builder构建Filter链路的好处,还在于它能自动加入Crossbar
    Filter(用于选择捕捉卡的输入端子,一般有三种:AV、S-Video、TV),如果是电视卡
    的话还有TV Tuner Filter等等;使用ICaptureGraphBuilder2::FindInterface就可以找
    到相应的控制接口等等。

    跟WDM卡相比,VFW卡实现的功能要简单得多。上述的Filter Graph创建过程,两种卡的
    处理是相似的;而对于视频捕捉的设置,则有较大的差异。WDM Capture Filter的执行
    文件为kswdmcap.ax,它实际上是kernel-mode下KsProxy的一个插件;而DirectShow使用
    了一个标识为CLSID_VfwCapture的Filter来支持VFW卡。WDM卡,设置Capture输出的图像
    格式、图像的对比度、亮度、色度、饱和度等,都是通过IAMStreamConfig、IAMVideoPr
    ocAmp等接口来实现(当然,在GraphEdit中可以通过Filter的Property Page来设置);
    而VFW卡,一般要将驱动程序内的设置对话框显示给用户。VFW驱动程序一般实现三个设
    置对话框:Video Source(设置图像源属性)、Video Format(设置图像输出格式)和V
    ideo Display(设置图像显示属性)。下面是显示Video Source对话框的示例:
    pControl->Stop(); // Stop the graph.
    // Query the capture filter for the IAMVfwCaptureDialogs interface.
    IAMVfwCaptureDialogs *pVfw = 0;
    hr = pCap->QueryInterface(IID_IAMVfwCaptureDialogs, (void**)&pVfw);
    if (SUCCEEDED(hr))
    {
    // Check if the device supports this dialog box.
    if (S_OK == pVfw->HasDialog(VfwCaptureDialog_Source))
    {
    // Show the dialog box.
    hr = pVfw->ShowDialog(VfwCaptureDialog_Source, hwndParent);
    }
    }
    pControl->Run();

    以上讲述了视频捕捉程序创建的一般过程。视频捕捉还有其他问题,比如AV同步、设备
    的热插拔、DV Camcorder的控制、Analog TV以及Digital TV的支持,还有捕捉后的音视
    频压缩、音视频合成,或者硬件压缩卡的支持等等。要想编写出专业级的视频捕捉程序
    ,这些问题是不可回避的!



    =============================================================================

    yellowdawn (追逐天边的云) (2005年01月26日18:37:14 星期三) 提到 :

    作者:陆其明 刊载于《中文信息——程序春秋》2003.10期
    关键字 DirectShow Audio Capture
    现在的所谓多媒体电脑一般都会有声卡(软声卡或硬声卡),有声卡就能进行音频的捕
    捉。大家一定熟悉Windows自带的附件“录音机”程序,可以通过麦克风进行录音,最终
    生成一个Wave文件。读完本文之后,你就会发现,自己使用DirectShow写一个音频捕捉
    的应用程序,原来也是这么的容易!
    大家知道,DirectShow对硬件的支持是通过特定的包装Filter来实现的。声卡使用的是A
    udio Capture Filter,Filter内部使用以waveIn开头的一套API实现(如waveInOpen等
    )。运行GraphEdit,插入Filter时,在“Audio Capture Sources”目录下,我们就能
    看到所有代表本地机器上的声卡的各个Filter(有的机器装了几张声卡,这里就会有几
    个Filter)。在Filter Graph中加入这个Filter,我们发现这个Filter有很多Input pin
    ,如Line In、CD Audio、Microphone、Stereo Mix等等;有一个Capture output pin。
    需要说明的是,在Filter Graph中,这些Input pin并没有真正的数据流入,它们只是声
    卡的各个输入端子的象征性表示;所以这些Input pin永远也不用连接。下面我们来看一
    下如何创建一个音频捕捉程序。首先,当然是加入一个Audio Capture Filter。大家知
    道,DirectShow加入一个硬件Filter,都是要靠“枚举”;声卡Filter也不例外。代表
    声卡的Filter都注册在CLSID_AudioInputDeviceCategory目录下,使用系统设备枚举器
    枚举这个目录,就能发现我们想要创建的声卡对象。(如何枚举这里就不再赘述了。)
    当成功加入声卡Filter后,接下去的问题就是要将这个Filter与其他Filter相连。比如
    ,我们想捕捉生成一个Wave文件,那么我们还需加入一个Wave Dest Filter和一个File
    Writer Filter,然后依次将它们相连。需要说明的是,Wave Dest Filter是微软Direct
    X SDK带的一个例子,在samples\Multimedia\DirectShow\Filters\WavDest目录下,我
    们必须首先编译这个例子并且注册这个Filter;这个Filter的功能是,当我们结束捕捉
    时,往Wave文件中写入一个文件头信息。下图是在GraphEdit中的Filter连接图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=AudioCap1.jpg下面是一段创建音频捕捉程序的框架代码,可供参考:
    void BuildAudioCaptureGraph(void) // Warning! No error checking here.
    {
    IBaseFilter *pSrc = NULL, *pWaveDest = NULL, *pWriter = NULL;
    IFileSinkFilter *pSink= NULL;
    IGraphBuilder *pGraph;
    // Create the Filter Graph Manager.
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void**)&pGraph);
    // Add the audio capture filter.
    FindAudioCapture(&pSrc); // Assume that this function enumerates
    // audio capture devices and picks one.
    pGraph->AddFilter(pSrc, L"Capture");
    // Add the WavDest and the File Writer.
    AddFilterByClsid(pGraph, L"WavDest", CLSID_WavDest, &pWavDest);
    AddFilterByClsid(pGraph, L"File Writer", CLSID_FileWriter, &pWriter);
    // Set the file name.
    pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
    pSink->SetFileName(L"C:\\MyWackyWav.wav", NULL);
    // Hook everything up.
    ConnectTwoFilters(pGraph, pSrc, pWavDest);
    ConnectTwoFilters(pGraph, pWavDest, pWriter);
    }
    当然,在进行音频捕捉的同时,我们还可以实时监听音频源的输入。如下示意图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=AudioCap2.jpg我们在Audio Capture Filter后面接了一个Infinite Pin Tee,这个Filter能够将一个I
    nput pin输入的数据,复制成多份,分别通过各个Output pin发送出去。(这个Filter
    也是微软DirectX SDK带的一个例子,在samples\Multimedia\DirectShow\Filters\
    InfTee目录下。)我们看到Tee Filter的一支连到了DirectSound Renderer,可以将声
    音放在声卡上输出。 创建音频捕捉的应用程序很简单吧!下面,我们还要来讨论一下音
    频捕捉前可能用到的一些参数设置。在声卡Filter的每个Input pin上,我们都可以得到
    IAMAudioInputMixer这个接口。通过这个接口,我们可以设置各个输入端子的音频属性
    ,如进行音频合成时是否允许某个输入端子的音频参与混合、音频输入的音量,还有Tre
    ble、Bass等等。另外,在Filter上也可以得到IAMAudioInputMixer接口,这时调用接口
    方法就可以统一控制各个输入端子的属性。音频捕捉,还可以设置的是音频的采样频率
    以及声音的具体格式(8Bits或16Bits,单声道或双声道)。我们可以通过Capture
    output pin的IAMStreamConfig来完成。下面的代码可供参考:
    HRESULT hr = pCapturePin->QueryInterface(IID_IAMStreamConfig, (void
    **)&pCfg);
    // Read current media type/format
    AM_MEDIA_TYPE *pmt={0};
    hr = pCfg->GetFormat(&pmt);
    if (SUCCEEDED(hr))
    {
    // Fill in values for the new format
    WAVEFORMATEX *pWF = (WAVEFORMATEX *) pmt->pbFormat;
    pWF->nChannels = (WORD) nChannels;
    pWF->nSamplesPerSec = nFrequency;
    pWF->nAvgBytesPerSec = lBytesPerSecond;
    pWF->wBitsPerSample = (WORD) (nBytesPerSample * 8);
    pWF->nBlockAlign = (WORD) (nBytesPerSample * nChannels);
    // Set the new formattype for the output pin
    hr = pCfg->SetFormat(pmt);
    DeleteMediaType(pmt);
    }
    // Release interfaces
    pCfg->Release();
    最后,还要提到的一点,也是音频捕捉比较特殊的地方:我们可以通过Capture
    output pin上的IAMBufferNegotiation接口,改变音频捕捉缓冲的大小,以减少声音播
    放的延迟。默认情况下,Audio Capture Filter使用0.5秒钟的缓冲。对于一些特殊的应
    用,这么大的缓冲是没有必要的,带来的延迟也比较大。一般,缓冲设置成能够容纳80
    毫秒的数据已经很可靠;甚至30-40毫秒也已经足够了。但是也不能太小,否则会影响到
    音频捕捉的效率,使音质受到损害。下面的代码设置音频捕捉的缓冲大小,可供参考:
    pCapturePin->QueryInterface(IID_IAMBufferNegotiation, (void **)&pNeg);
    // Set the buffer size based on selected settings
    ALLOCATOR_PROPERTIES prop={0};
    prop.cbBuffer = lBufferSize;
    prop.cBuffers = 6;
    prop.cbAlign = nBytesPerSample * nChannels;
    hr = pNeg->SuggestAllocatorProperties(&prop);
    pNeg->Release();
    以上,我们讲述了音频捕捉程序的创建过程,以及一些捕捉参数的设置方法。相信大家
    对于如何写音频捕捉程序已经有了自己的认识。音频捕捉直接得到的是PCM数据,根据需
    要,我们还可以对其进行压缩,比如用Mp3格式(微软提供了一个免费的Mp3 Encoder)
    、AC3格式等等;压缩后数据量更少,可以符合很多场合的应用。


    =============================================================================

    yellowdawn (追逐天边的云) (2005年01月26日18:41:02 星期三) 提到 :

    作者:陆其明
    关键字 DirectShow TV Capture
    现在市面上的很多视频捕捉卡都带有电视接收功能(以下简称TV),比如ATI TV Wonder
    等。能够在电脑上接收电视(注意:本文提及的电视均指模拟电视),并且把电视节目
    保存到文件,这是件多么美妙的事情!所以,我们有必要来探讨一下这个问题。关于如
    何编写视频捕捉程序,笔者以前的一篇文章(《DirectShow应用——视频捕捉WDM Vs
    VFW》),已经作了较为详尽的介绍。本文的侧重点,在于介绍如何使用视频捕捉卡的TV
    功能。
    大家先来看一下,带有TV功能的Capture Filter Graph,实物参考图如下:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=TVFilters.jpg其中,橙色的Filter为Capture Filter,它前面有两个Crossbar用以切换输入端子(一
    张典型的捕捉卡有三个输入端子:AV端子、S-Video端子和TV端子),ATI TV Tuner即为
    本文要重点介绍的带有TV接收功能的Filter。TV Tuner的属性页如下:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=TVProp.jpg通过这个属性页界面,我们就可以选择频道来观看电视了!
    大家可能觉得,直接将TV Tuner的属性页暴露给用户,这种做法太不专业了!是的,下
    面我们就来看一下如何用程序来实现TV的上述控制功能。
    先来看一下Filter Graph的创建。大多数情况下,我们使用IGraphBuilder接口来实现。
    但对于视频捕捉应用,推荐使用ICaptureGraphBuilder2接口,它将大大简化我们的工作
    。我们可以通过接口方法ICaptureGraphBuilder2::SetFiltergraph设置我们已经创建的
    Filter Graph,然后,让ICaptureGraphBuilder2来完成其他繁琐的工作,比如加入Cros
    sbar Filter、加入TV Tuner Filter、连接Capture Filter的各个Output pin等。而这
    些工作,都将随着ICaptureGraphBuilder2::RenderStream的调用自动完成,参考如下:
    // Video:
    pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
    pCaptureFilter, NULL, NULL);
    // Audio:
    pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Audio,
    pCaptureFilter, NULL, NULL);
    Filter Graph创建完成后,我们接着需要通过Crossbar Filter的IAMCrossbar接口,来
    选择TV作为我们的输入端子(具体做法,参见SDK例子AmCap中实现的CCrossbar类)。再
    然后,我们就可以通过TV Tuner的一个接口去实现TV的控制功能,这个接口就是IAMTVTu
    ner了。获得这个接口的方法不是找到这个Filter,然后调用QueryInterface,而是要用
    到ICaptureGraphBuilder2的另一个方法:FindInterface。参考如下:
    IAMTVTuner *pTuner = NULL;
    hr = pBuild->FindInterface(
    &LOOK_UPSTREAM_ONLY, // Look upstream from pCap.
    NULL, // No particular media type.
    pCap, // Pointer to the capture filter.
    IID_IAMTVTuner, (void**)&pTuner);
    if (SUCCEEDED(hr))
    {
    // Use pTuner ...
    pTuner->Release();
    }
    关于模拟电视的一些常识,以及IAMTVTuner的各个接口方法的使用细节,请参考DirectX
    的帮助文档。笔者这里只介绍常用的几个接口方法,如下:
    IAMTuner::put_CountryCode,设置电视接收的国家代码(中国是86);
    IAMTuner::put_Mode,设置接收器是电视模式还是调频电台等模式;
    IAMTVTuner::put_InputType,设置电视是天线输入还是有线电视输入;
    IAMTVTuner::get_TVFormat,得到电视的制式是PAL、NTSC、SECAM;
    IAMTVTuner::AutoTune,进行自动频道搜索。
    好了,大概介绍完了。使用这个Filter Graph,我们可以观看电视,也可以将电视捕捉
    下来生成文件保存。很容易吧?!其实这些都是DirectShow的功劳!


    =============================================================================

    yellowdawn (追逐天边的云) (2005年01月26日18:43:19 星期三) 提到 :

    作者:陆其明 刊载于《中文信息——程序春秋》2003.10期
    关键字 DirectShow DVD
    一. DVD基础知识
    首先,我们来增加一点感性认识,看一下DVD光盘的文件系统(采用MicroUDF标准)。我
    们可以看到,典型情况下光盘上有两个文件夹:VIDEO_TS和AUDIO_TS(通常是空的,这
    里不作介绍)。VIDEO_TS下面一般包含三种类型的文件:.VOB文件、.IFO文件和.BUP文
    件。这些文件都是作什么用的呢?其实,.VOB文件是用来保存所有MPEG2格式的音视频数
    据的,这些数据包括影片内容、供菜单(Menu)和按钮(Button)以及多种语言字幕用
    的子图片(Sub-picture)流;.IFO文件则是控制.VOB文件播放用的,这个文件中可以找
    到有关何时以及如何播放.VOB文件数据的控制信息;而.BUP文件则是.IFO文件内容的一
    个备份(因为.IFO文件对于保证DVD光盘的正确播放起着至关重要的作用)。根据这三种
    文件的特性,我们也就可以理解它们在光盘介质上的排列顺序了:IFO-VOB-VOB...-BUP

    我们再来看一下各个具体的文件。VIDEO_TS.IFO文件,保存DVD光盘的视频管理器 (Vid
    eo Manager,简称VMG)信息,主要是光盘的一些全局信息,比如光盘指定的播放区域、
    如何显示菜单等。VIDEO_TS.VOB文件,保存显示菜单的数据。类似VTS_xx_y.VOB的文件
    ,保存各个视频节目。(注:这里"xx"是节目编号,从01到99,"y"是从0到9的编号;由
    于MicroUDF文件系统中一个文件最大只能1 GB,因此大多数影片不得不保存在多个VOB文
    件中。)VTS_xx_y.IFO文件,保存对应编号的VOB文件的音视频格式信息。
    大致了解了文件系统,我们再来看一下DVD其他方面的基础知识。大家知道,一张DVD光
    盘主要包括三种媒体数据:视频(Video)、音频(Audio)和子图片。视频一般采用MPE
    G2压缩算法,视频流支持最多9个视角(Angle),支持Line 21 Closed Caption(模拟
    电视中的概念,主要是些文字信息);音频最多支持8条不同的流(即8种不同语言的配
    音),支持最多6声道的声音格式(具体格式可以是AC3、MPEG、LPCM、DTS、SDDS等),
    支持卡拉OK;子图片流最多可以支持32个(即提供多种语言的字幕)。DVD内容最主要的
    逻辑分类叫做“标题”(Title)(一个标题通常代表了一部电影,或者一段视频节目,
    一张DVD光盘最多可以有99个标题),每个标题又可以 最多分成999个“章节”(Chapte
    r)(章节是便于用户随机访问的节点,每播放完一个章节的内容会自动播放下一个章节
    ,或者跳回菜单)。
    用户与DVD的交互,最主要是通过菜单来完成的。菜单有两种:视频管理器菜单(Video
    Manager Menu,简称VMGM,也叫做Top Menu或Title Menu)和视频标题集菜单(Video
    Title Set Menu,简称VTSM,也叫做Root Menu,尽管它不是真正意义上的“根”菜单)
    。VMGM允许用户进入主要的标题或者标题集(包含有一组标题)。如果进入标题集,就
    显示VTSM子菜单。VTSM菜单可以包含进入当前标题集中各个标题的按钮,还有音频、视
    角、字幕、章节等选择子菜单。VTSM菜单是可选的,可以不实现;当用户开始播放一张D
    VD,一般首先看到的就是VMGM菜单,除非这张光盘在制作时被设置成了自动播放第一个
    标题(此时DVD也可以根本不制作VMGM菜单)。
    DVD还有父母锁功能。DVD中的视频内容可以打上父母管理级别(Parental Management
    Level,简称PML);级别可以从1到8,其中1是无限制级别,8是最高限制级别。这样,
    可以禁止孩子在未经父母同意的情况下观看电影。注意:父母锁功能需要播放器的
    支持。
    DVD导航(Navigation)还有一个非常重要的抽象概念,就是域(Domain)。通俗地说,
    也就是DVD在特定播放状态下的操作许可。比如“选择按钮”命令必须在菜单状态下才有
    效,“快进”命令在停止或者菜单状态下是无效的。播放器必须使用域的概念,阻止用
    户向DVD驱动器发送无效的命令。下表列出了五个主要的域以及对应的操作说明:
    Domain(域)

    DVD导航器的工作内容

    First Play(开始播放DVD时第一个播放的一段视频内容) 读取光盘上初始部分
    Video Manager Menu(显示VMGM菜单时) 读取光盘的主菜单,菜单相关的指令都是有效

    Video Title Set Menu(显示VTSM菜单时) 读取VTSM菜单,或者设置音频、字幕、视角
    等的子菜单,菜单相关的指令都是有效的
    Title(播放标题内容时) 菜单相关的指令都是无效的 
    Stop(停止时) 导航器不在工作,此时可以执行播放指令
    另外,DVD的制作者还可以利用“用户操作控制”功能(User Operation Controls,简
    称UOPs)来限制用户的操作。这需要在光盘上记录这些限制记号。比如,大多数光盘都
    不允许用户在DVD播放处于First Play域时执行快进或者显示菜单命令。
    二. DirectShow对DVD的支持
    DirectShow对DVD播放提供了强力的支持。(DirectShow为支持DVD播放作了大量的工作
    ,除了不提供MPEG2 Video Decoder Filter外。换句话说,必须提供第三方的MPEG2
    Decoder,否则我们写的播放程序还是跑不起来的!)编写DVD播放程序,我们无须研究D
    VD的规格说明书;了解上面的这些基础知识就已经足够了。因为微软提供了一个叫DVD
    Navigator的Filter,帮我们完成了繁琐的DVD导航的任务;提供一个专门用于建立播放D
    VD的Filter Graph的COM组件(CLSID_DvdGraphBuilder)。我们所要做的,主要就是使
    用DVD Navigator的两个接口:IDvdInfo2,获得光盘的属性和导航状态;以及IDvdContr
    ol2,设置属性和执行播放操作。典型的播放DVD的Filter Graph如下:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=DVDPlayback.jpg典型的Filter Graph创建过程如下:
    // Create an instance of the DVD Graph Builder object.
    HRESULT hr;
    hr = CoCreateInstance(CLSID_DvdGraphBuilder,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_IDvdGraphBuilder,
    reinterpret_cast<void**>(&m_pIDvdGB));
    // Build the DVD filter graph.
    AM_DVD_RENDERSTATUS buildStatus;
    hr = m_pIDvdGB->RenderDvdVideoVolume(pszwDiscPath, m_dwRenderFlags,
    &buildStatus);
    // Get the pointers to the DVD Navigator interfaces.
    hr = m_pIDvdGB->GetDvdInterface(IID_IDvdInfo2,
    reinterpret_cast<void**>(&m_pIDvdI2));
    hr = m_pIDvdGB->GetDvdInterface(IID_IDvdControl2,
    reinterpret_cast<void**>(&m_pIDvdC2));
    ...
    // Get a pointer to the filter graph manager.
    hr = m_pDvdGB->GetFiltergraph(&m_pGraph);
    ...
    // Use the graph pointer to get a pointer to IMediaControl,
    // for controlling the filter graph as a whole.
    hr = m_pGraph->QueryInterface(IID_IMediaControl,
    reinterpret_cast<void**>(&m_pIMC));
    ...
    // Get a pointer to IMediaEventEx,
    // used for handling DVD and other filter graph events.
    hr = m_pGraph->QueryInterface(IID_IMediaEventEx,
    reinterpret_cast<void**>(&m_pME));
    ...
    // Use the graph builder pointer again to get the IVideoWindow interface,
    // to set the window style and message-handling behavior of the video
    renderer filter.
    hr = m_pIDvdGB->GetDvdInterface(IID_IVideoWindow,
    reinterpret_cast<void**>(&m_pIVW));
    hr = m_pDvdGB->GetDvdInterface(IID_IAMLine21Decoder,
    reinterpret_cast<void**>(&pL21Dec));
    关于IDvdInfo2和IDvdControl2的各个接口方法的详细说明和用法,请参见DirectShow
    SDK文档,以及SDK提供的例子代码DVDSample。下面,仅就编写DVD播放程序需要注意的
    地方,进行一些简单的罗列:
    1. 为了得到DVD Navigator在创建时发出的事件,一般在RenderDvdVideoVolume 调用之
    前获得ImediaEventEx接口。
    2. 菜单命令实际上有两种,一种是选中(Select),一种是激活(Activate)。前者如
    IDvdControl2::SelectAtPosition,IDvdControl2::SelectButton,IDvdControl2::Sel
    ectRelativeButton等,效果是高亮度显示被选中的菜单;后者如IDvdControl2::Activa
    teAtPosition,IDvdControl2::ActivateButton等,效果是产生相应的动作。也有选中
    加激活的命令,如IDvdControl2::SelectAndActivateButton。
    3. DVD最多支持8条音频流、32条子图片流,但在同一时刻,都只能各自选中某一条。
    4. DVD Navigator本身并不强调父母锁功能,而只是把光盘上的PML信息发送给应用程序
    。因此,父母锁功能需要在应用程序上完成。
    5. 通过IDvdInfo2::GetState调用,可以得到IDvdState对象,用以实现“书签”的功能
    。具体实现参见DVDSample的CDvdCore::SaveBookmark和CDvdCore::RestoreBookmark两
    个函数。
    6. 注意DVD播放时候的Filter Graph状态,需要同时考虑DVD Navigator的状态。
    7. 支持卡拉OK的DVD,要求Audio Decoder实现AM_KSPROPSETID_DvdKaraoke属性集(即
    实现IKsPropertySet接口)。
    8. IDvdControl2关于播放的接口方法,一般都有异步和同步两种调用方式。如果是异步
    方式,调用这些接口方法后,会立即返回,而并不等到实际的操作完成。有时候,这样
    操作会引起DVD Navigator的状态混乱。对于这些接口方法,微软推荐了有5种调用方法
    ,下面列出常用的3种:
    (1)异步方式(以PlayTitle调用为例,下同)
    HRESULT hr = pDVDControl2->PlayTitle( uTitle,
    DVD_CMD_FLAG_None, // = 0
    NULL);
    (2)阻塞方式
    HRESULT hr = pDVDControl2->PlayTitle( uTitle,
    EC_DVD_CMD_FLAG_Block,
    NULL);
    (3)使用同步对象
    IDvdCmd* pObj;
    HRESULT hr = pDVDControl2->PlayTitle(uTitle, 0, &pObj);
    if(SUCCEEDED(hr))
    {
    pObj->WaitToEnd();
    pObj->Release();
    }
    三. 总结
    总之,DirectShow使我们从DVD的专业知识中解放出来;有了DirectShow的支持,编写一
    个DVD播放程序还是比较轻松的!再次提醒读者朋友们,必须要提供第三方的Video
    Decoder和Audio Decoder,否则,我们的DVD播放程序会陷入“万事具备,只欠东风”的
    窘境!


    =============================================================================

    yellowdawn (追逐天边的云) (2005年01月26日18:48:27 星期三) 提到 :

    作者:陆其明
    关键字 DirectShow DV Capture
    DV数码摄像机(以下简称DV机),随着人民生活水平的不断提高,正逐步进入百姓家庭
    中。如何将拍在磁带上的视频内容,转成媒体文件,乃至刻成光盘,是个值得探讨的问
    题,也是一件很有意思的问题。还好,因为我们选择了DirectShow,这一切看起来是那
    么的简单!
    DV机通过1394接口与电脑相连(如果你的电脑没有1394接口的话,需要装一张1394转接
    卡);支持即插即用。使用DirectShow的一个工具软件GraphEdit,我们可以在“Video
    Capture Sources”目录下看到一个名为“Microsoft DV Camera and VCR”的Filter,
    即代表我们的DV机。微软提供的DV相关的Filter还有DV Splitter、DV Video Decoder、
    DV Video Encoder、DV Muxer等。DV数据,视频采用帧内压缩,音频仍然为PCM格式,采
    用交叉的方式来存储;PAL制的图像大小为720x576,单帧为144000字节,NTSC制的图像
    大小为720x480,单帧为120000字节;所以DV数据保存为文件,文件还是很大的。
    一般DV数据可以直接保存到AVI文件中。有两种格式,一种格式(Type-1)仍然将音视频
    按DV原有的交叉格式保存,另一种格式(Type-2)将DV数据的音视频分开后再保存(这
    种格式视频部分其实仍然带有音频的冗余数据)。两种格式各有优缺点:Type-1可以节
    省CPU使用时间,适合实时捕捉的场合;而Type-2保持了对VFW的向后兼容性。
    接下去,我们来看一下如何来创建我们的DV应用程序。第一步,如何通过程序去控制DV
    机?DV机可以有两种工作模式,一种是摄像模式,一种是回放模式。我们这里说的“控
    制”,主要是指如何控制DV机对磁带的一系列操作(如播放、暂停、快进、快退、停止
    等)。答案很简单,主要是通过DV Filter上的IAMExtTransport接口来实现的。(需要
    提醒一下的是,创建DV Filter,必须通过系统枚举器枚举视频捕捉设备来完成;如何枚
    举系统设备,请参考DirectX帮助文档,这里不再赘述。)下面是获得这个接口的参考代
    码:
    HRESULT hr;
    IBaseFilter *pDVCam; // Pointer to the capture filter.
    IAMExtTransport *pTransport;
    // Create an instance of the capture filter (not shown).
    hr = pDVCam->QueryInterface(IID_IAMExtTransport, (void **)&pTransport);
    获得这个接口后,我们就可以通过其接口方法put_Mode来实现对DV机播放的控制了。相
    应地,通过get_Mode也可以得到当前DV机的状态,如下:
    LONG State;
    hr = MyDevCap.pTransport->get_Mode(&State);
    if (SUCCEEDED(hr))
    {
    switch (State)
    {
    case ED_MODE_PLAY: // playing
    break;
    case ED_MODE_STOP: // stopped
    break;
    case ED_MODE_FREEZE: // paused
    break;
    }
    }
    除了IAMExtTransport接口外,还有其他两个控制接口:IAMExtDevice接口,可以获得DV
    机当前的工作模式,参考如下:
    IAMExtDevice *pDevice;
    hr = pDVCam->QueryInterface(IID_IAMExtDevice, (void **)&pDevice);
    LONG lDeviceType = 0;
    pDevice->GetCapability(ED_DEVCAP_DEVICE_TYPE, &lDeviceType, 0);
    if (lDeviceType == ED_DEVTYPE_VCR)
    {
    // Device is a VCR. Enable all VCR functions.
    }
    else
    {
    // Device is a camera.
    // Enable record and record-pause; disable other functions.
    }
    IAMTimecodeReader接口,可以获得每一个DV帧的时间戳。
    第二步,我们就来看一下DV的各种实际应用场合。如下图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=DVType1Cap.jpg我们可以将DV Filter输出的DV数据直接以Type-1的方式保存为AVI文件。如上图所示,M
    SDV即为DV Filter,后面紧跟着接了一个Smart Tee Filter,将一路DV流分成了两路;
    这样,我们在写文件的同时又能够预览,一举两得。以上的Filter Graph结构,是在视
    频捕捉场合最常用的。当然,我们也可以将DV数据解码后再保存,如下图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=DVRGBCap.jpg其中,Infinite Pin Tee Filter的功能类似于Smart Tee,是SDK中提供的一个带有源码
    的Filter;两者的区别主要在于它们对输出数据的时间戳处理上不同。上面我们讲了两
    种从DV机上抓取数据的应用,那么,能不能将我们电脑上的媒体文件写到DV机的磁带上
    呢?答案是肯定的。请看如下参考图:
    http://bbs.njupt.edu.cn/cgi-bin/showfile?name=FileToDVDev.jpg这里假设我们的AVI文件包含的视频格式是DV。(如果不是DV格式,我们需要用DV
    Video Encoder将视频压缩成DV格式后,再往下连接。)我们还用到了DV Mux Filter,
    将音频和视频流交互打包成标准的DV帧,然后送到DV Filter进行录像。
    好了,讲到这,大家对DV的应用编程应该有所了解了吧?!更多的细节,还要参考Direc
    tX的帮助文档,以及SDK中一个叫DVApp的例子,研究一下吧,可以加深理解哦!

    ================================= 合集结束 ==================================

  • 相关阅读:
    Python基础-16生成器-迭代器
    Python基础-15模块-包-库
    Python基础-14异常处理
    Python基础-13面向对象
    Python基础-12解析式
    Python 入门日记(十)—— 文件和异常
    Python 入门日记(九)—— 类
    Python 入门日记(八)—— 函数
    Python 入门日记(七)—— 用户输入和 while 循环
    Python 入门日记(六)—— 字典
  • 原文地址:https://www.cnblogs.com/freeliver54/p/1311488.html
Copyright © 2011-2022 走看看