• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Delphi的DirectShow开发概述

原作者: [db:作者] 来自: [db:来源] 收藏 邀请
第一部分:背景知识
DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。它经过DirectX 6.0中的DirectX Media发展而来,集成了DirectX家族中的其他成员(DirectDrawDirectSound等),可以说是一位集大成者            
DirectShow能做些什么? DirectShow为多媒体流的捕捉和回放提供了强有力的支持。运用DirectShow,可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括AsfMpegAviDvMp3Wave等等,使得多媒体数据的回放变得轻而易举。另外,DirectShow直接支持DVD的播放,视频的非线性编辑,以及与数字摄像机的数据交换。更值得一提的是,DirectShow提供的是一种开放式的开发环境,每个功能模块都采取COM组件方式,称为Filter,开发者也可以开发自己的功能Filter来扩展DirectShow的应用。按照功能来划分,Filter分为3类:Source Filter, Transform Filter, Rendering Filter。前者负责获取数据,数据源可以是文件、数字摄像机等,然后将数据往下传输;中间者负责数据的格式转换,比如数据流的分离与合成、编码解码等,然后把数据继续往下传输;后者负责数据的去向——给声卡、显卡进行播放或者输出到文件存储。
第二部分核心技术
DirectShow的开发实际就是Filter的开发,DirectShow自身提供了,下面就是Filter概述。
1DirectShow Filter
Filter
一般分为下面几种类型。

  (1)源过滤器(source filter):源过滤器引入数据到过滤器图表中,数据来源可以是文件、网络、照相机等。不同的源过滤器处理不同类型的数据源。

  (2)变换过滤器(transform filter):变换过滤器的工作是获取输入流,处理数据,并生成输出流。变换过滤器对数据的处理包括编解码、格式转换、压缩解压缩等。

  (3)提交过滤器(renderer filter):提交过滤器在过滤器图表里处于最后一级,它们接收数据并把数据提交给外设。

  (4)分割过滤器(splitter filter):分割过滤器把输入流分割成多个输出。例如,AVI分割过滤器把一个AVI格式的字节流分割成视频流和音频流。

  (5)混合过滤器(mux filter):混合过滤器把多个输入组合成一个单独的数据流。例如,AVI混合过滤器把视频流和音频流合成一个AVI格式的字节流。

  过滤器的这些分类并不是绝对的,例如一个ASF读过滤器(ASF Reader filter)既是一个源过滤器又是一个分割过滤器。

2
、关于Filter Graph Manager

  Filter Graph Manager也是一个com对象,用来控制Filter graph中的所有的filter,主要有以下的功能:

  1) 用来协调filter之间的状态改变,从而使graph 中的所有的filter的状态的改变应该一致。

  2) 建立一个参考时钟。

  3) filter 的消息通知返回给应用程序

  4) 提供用来建立 filter graph的方法。

     
简单描述,Graph就是各个Filter组成的一个流程图。
      SourceFilter----|-----SpliterFilter-------------(Video-pin)>-----TransFormFilter--->VideoRender
                                           |---------------------(Audio-pin)->----ACMWraperFilter--->DirectSoundFilter
程序启动过程,先创建各个filtercom对象,然后使用FilterGraph.Addfilter加入到Graph中,然后把每个Filter按照
数据流把OutPininpuin连接起来。最好启动FilterGraph.play即可。

  Directshow是基于模块化,每个功能模块都采取COM组件方式,称为FilterDirectshow提供了一系列的标准的模块可用于应用开发,开发者也可以开发自己的功能Filter来扩展Directshow的应用。下面我们用一个例子来说明如何采取Filter来播放一个AVI的视频文件。

  1) 首先从一个文件中读取AVI数据,形成字节流。(这个工作由源Filter完成)

  2) 检查AVI数据流的头格式,然后通过AVI分割Filter将视频流和音频流分开。

  3) 解码视频流,根据压缩格式的不同,选取不同的decoder filters

  4) 通过Renderer Filter重画视频图像。

  5) 音频流送到声卡进行播放,一般采用缺省的 DirectSound Device Filter 
 
状态改变,Graph中的filter的状态改变应该一致,因此,应用程序并将状态改变的命令直接发给filter,而是将相应的状态改变的命令发送给Filter graph Manager,由manager将命令分发给graph中每一个filterSeeking也是同样的方式工作,首先由应用程序将seek命令发送到filter graph 管理器,然后由其分发给每个filter

  参考时钟,graph中的filter都采用的同一个时钟,称为参考时钟(reference clock),参考时钟可以确保所有的数据流同步,视频桢或者音频桢应该被提交的时间称为presentation time.presentation time 是相对于参考时钟来确定的。Filter graph Manager应该选择一个参考时钟,可以选择声卡上的时钟,也可以选择系统时钟。

  Graph事件, Graph 管理器采用事件机制将graph中发生的事件通知给应用程序,这个机制类似于windows的消息循环机制。

  Graph构建的方法,graph管理器给应用程序提供了将filter添加进graph的方法,连接filter的方法,断开filter连接的方法。

  但是,graph 管理器没有提供如何将数据从一个filter发送到另一个filter的方法,这个工作是由filter在内部通过pin来独立完成的。
3、媒体类型

  因为Directshow是基于com组件的,就需要有一种方式来描述filter graph每一个点的数据格式,例如,我们还以播放AVI文件为例,数据以RIFF块的形式进入graph中,然后被分割成视频和音频流,视频流有一系列的压缩的视频桢组成,解压后,视频流由一系列的无压缩的位图组成,音频流也要走同样的步骤。
媒体类型是一种很普遍的,可以扩展的用来描述数字媒体格式的方法,当两个filter连接的时候,他们会就采用某一种媒体类型达成一致的协议。媒体类型定义了处于源头的filter将要给下游的filter发送什么样的数据,以及数据的physical layout。如果两个filter不能够支持同一种的媒体类型,那么他们就没法连接起来。

  对于大多数的应用来说,也许你不用考虑媒体类型,但是,有些应用程序中,你会直接应用到媒体类型的。

  媒体类型是通过AM_MEDIA_TYPE结构定义的。
Filters通过pin的连接来传递数据,数据流是从一个filter的输出pin流向相连的filter的输入pin。输出pin常用的传递数据的方式是调用输入pin上的IMemInputPin::Receive方法。
对于filter来说,可以有好几种方式来分配媒体数据使用的内存块,可以在堆上分配,可以在DirectDraw的表面,也可以采用GDI共享内存,还有其他的一些方法,在Directshow中用来进行内存分配任务的是内存分配器(allocator),也是一个COM对象,暴露了一个IMemAllocator接口。

  当两个pin连接的时候,必须有一个pin提供一个allocatorDirectshow定义了一系列函数调用用来确定由哪个pin提供allocator,以及buffer的数量和大小。

  在数据流开始之前,allocator会创建一个内存池(pool of buffer),在开始发送数据流以后,源filter就会将数据填充到内存池中一个空闲的buffer中,然后传递给下面的filter。但是,源filter并不是直接将内存buffer的指针直接传递给下游的filter,而是通过一个media samplesCOM对象,这个sampleallocator创建的用来管理内存bufferMedia sample暴露了IMediaSample接口,一个sample包含了下面的内容:

  一个指向没有发送的内存的指针。

  一个时间戳

  一些标志

  媒体类型。

  时间戳表明了presentation timeRenderer filter就是根据这个时间来安排render顺序的。标志是用来标示数据是否中断等等,媒体类型提供了中途改变数据格式的一种方法,不过,一般sample没有媒体类型,表明它们的媒体类型一直没有改变。

  当一个filter正在使用buffer,它就会保持一个sample的引用计数,allocator通过sample的引用计数用来确定是否可以重新使用一个buffer。这样就防止了buffer的使用冲突,当所有的filter都释放了对sample的引用,sample才返回到allocator的内存池,供重新使用
基于DelphiDirectShow开发概述2
MajorType主要类型;例如视频,音频,还是位流
subType  : 
辅助说明类型,例如视频中的YUV12,还是UYVY等等
formatType:
格式描述,更为细节的结构体。例如,视频大小,频率,帧率等,
           
可以使用FORMAT_VIDEOINFO(VIDEOINFOHEADER)FORMAT_WAVEFORMATEX(WAVEFORMATEX)结构体来描述
//PAMMediaType
当使用AM_MEDIA_TYPE数据结构来描述媒体类型的时候,如果MajorTypesubTypeformatType都指定了GUID,那么
这就是完全媒体类型。 
***************************************Filter的连接************************************
   Filter
的连接实际上就是Pin的连接。连接方向总是由上一级的Filter(UpStream Filter)的输出Pin指向下一级
Filter
DownStream Filter)的输入Pin
1.Filter连接过程
  Pin
也是一种COM接口。实现了IPIN的接口。一般通过调用(下面的函数来连接)
  IFilterGraph.ConnectDirect
IGraphBuilder.ConnectIGraphBuilder.RenderIGraphBuilder.RenderFile
 
  {
下面就是个范例,一般Filter是在停止状态下连接的。
 
      //
连接  source -> MPEG1Spliter
    Source.FindPin(StringToOLEStr('Output'), OutPin);
    MPEG1Splitter.FindPin(StringToOLEStr('Input'), inPin);
    hr := (FilterGraph1 as IGraphBuilder).Connect(OutPin, InPin);
    if FAILED(hr) then begin
      ShowMessage('Failed connect mpg Source -> MPEG1Splitter');
      exit;
    end;
   }
2.FilterGraph构建的方法
  1)IFilterGraph.AddFilter  
该参数提供一个Filter对象,将其加入到FilterGraph.
  2)IFilterGraph.ConnectDirect 
该参数提供输出Pin,输入Pin以及媒体类型,进行直接连接
  3)IGraphBuilder.AddSourceFilter
该参数提供源文件名,自动将一个SourceFilter加载到FilterGraph
  4)IGraphBuilder.Connect
该参数提供输出Pin,输入Pin以及媒体类型,进行连接,如果失败,自动尝试在中间加入必要的格式转换Filter
  5)IGraphBuilder.Render
该参数提供输出Pin,自动间加入必要的Filter完成剩下的部分FilterGraph的构建(直到连到RenderFilter上)
  6)IGraphBuilder.Render
该参数提供源文件名,自动间加入必要的Filter完成这个文件的回放
  {
    //
下面范例,表示该FilterGraph中有6Filter,他们都是由COM对象创建而来。
 var
    Source        : IBaseFilter; 
    MPEG1Splitter : IBaseFilter;
    MpegVcodec    : IBaseFilter;
    AviDec        : IBaseFilter;
    AviDest       : IBaseFilter;
    Writer        : IBaseFilter;
    hr            : HRESULT;
    OutPin, InPin : IPin;
    begin
    CoCreateInstance(CLSID_AsyncReader, nil, CLSCTX_INPROC,IID_IBaseFilter, Source); //
典型的拉模式
    CoCreateInstance(CLSID_MPEG1Splitter, nil, CLSCTX_INPROC,IID_IBaseFilter, MPEG1Splitter); //MPEG1
格式
    CoCreateInstance(CLSID_CMpegVideoCodec, nil, CLSCTX_INPROC,IID_IBaseFilter, MpegVcodec);  //MPEG
编码
    CoCreateInstance(CLSID_AVIDec, nil, CLSCTX_INPROC,IID_IBaseFilter, AviDec);    //AVI
解码
    CoCreateInstance(CLSID_AviDest, nil, CLSCTX_INPROC, IID_IBaseFilter, AviDest);  //AVI
目标
    CoCreateInstance(CLSID_FileWriter, nil, CLSCTX_INPROC,IID_IBaseFilter, Writer);  //
写文件
   
     (FilterGraph1 as IFilterGraph).AddFilter(Source, 'Source');
     (FilterGraph1 as IFilterGraph).AddFilter(MPEG1Splitter, 'MPEG1Splitter');
     (FilterGraph1 as IFilterGraph).AddFilter(MpegVcodec, 'MpegVcodec');
     (FilterGraph1 as IFilterGraph).AddFilter(AviDec, 'AviDec');
     (FilterGraph1 as IFilterGraph).AddFilter(AviDest, 'AviDest');
     (FilterGraph1 as IFilterGraph).AddFilter(Writer, 'Writer');
   end;
  } 
 
3.
一般使用GraphEdit可以查看到目前正常安装在系统中的Filter,如果是安装在DirectShow目录下的可以通过指定CLSID
  
CoCreateInstance来创建。在其它目录下的,必须通过系统枚举来创建。
  
系统提供了一个CLSID_SystemDeviceEnum,用CoCreateInstance创建,并获取ICreateDevEnum接口。然后
  
使用ICreateDevEnum.CreateClassEnumerator为指定的类型目录创建一个枚举器,并获得IEnumMoniker接口。
  
使用IEnumMoniker.next方法,媒体该目录下所有可用设备标识(Device Moniker,每个设备标识对象上都实现了Imoniker接口
  
调用Imoniker.bindtoStorage之后就可以访问设备标识属性集。比如得到设备的显示名字。
  
调用Imoniker.BindToObject可以将设备标识绑定成一个DirecshowFilter,然后调用IFilterGraph.addFilter加入FilterGraph
  
参加工作。
  
   DirectShow
通常有两个名字:显示名字例如:@device:cm:{33D9A760-90C8-11D0-BD43-00A0C911CE86}\xvid
                            
友好名字例如:xvid mpeg4 decoder
   {
下面就是调用代码
        
    var
      i, j: integer;
      AMoniker, MyMoniker: IMoniker;
      PropBag: IPropertyBag;
      AVariant: OleVariant;
      CLSID: TGUID;
      Found: boolean;
    begin
      for i := 0 to SysDevEnum.CountCategories - 1 do
        cbCategories.Items.Add(SysDevEnum.Categories[i].FriendlyName); //SysDevEnum:TSysDevEnum;
      Found := false;
      j := 0;
      MyMoniker := Filter.BaseFilter.Moniker;
      if MyMoniker = nil then exit;
      MyMoniker.BindToStorage(nil,nil,IPropertyBag, PropBag);
      if PropBag.Read('CLSID',AVariant,nil) = S_OK then
           CLSID := StringToGUID(AVariant)
      else CLSID := GUID_NULL;
      for i := 0 to SysDevEnum.CountCategories - 1 do
      begin
        SysDevEnum.SelectIndexCategory(i);
        if SysDevEnum.CountFilters > 0 then
          for j := 0 to SysDevEnum.CountFilters - 1 do
          begin
            if IsEqualGUID(CLSID, SysDevEnum.Filters[j].CLSID) then
              begin
                AMoniker := SysDevEnum.GetMoniker(j);
                Found := AMoniker.IsEqual(MyMoniker) = S_OK;
                AMoniker := nil;
              end;
            if Found then Break;
          end;
        if Found then
        begin
          cbCategories.ItemIndex := i;
          cbCategoriesChange(nil);
          lbFilters.ItemIndex := j;
          lbFiltersClick(nil);
          break;
        end;
      end;
      PropBag := nil;
      MyMoniker := nil;
  
   }
  
  PAMMediaType = ^TAMMediaType;
  _AMMediaType = record
    majortype            : TGUID;
    subtype              : TGUID;
    bFixedSizeSamples    : BOOL;
    bTemporalCompression : BOOL;
    lSampleSize          : ULONG;
    formattype           : TGUID;
    pUnk                 : IUnknown;
    cbFormat             : ULONG;
    pbFormat             : Pointer;
  end;
 
Filter
开发基础----基类分析
 1
TBCBaseFilter
  TBCBaseFilter = class(TBCUnknown, IBaseFilter, IAMovieSetup)
 
是最基本Filter的基类,使用方法:
 
1声明一个新类继承自  TBCBaseFilter
 
2在新类中定义一个FilterPin的实例。(PinTBCBasePin继承)
 
3实现纯虚函数TBCBaseFilter.GetPin,用于返回Filter上各个Pin的对象指针
 
4实现纯虚函数TBCBaseFilter.GetPinCount

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Matlab 排列组合发布时间:2022-07-18
下一篇:
BP神经网络算法及Matlab实现发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap