编写DirectShow Filters—动态格式改变

当两个filter 连接时,他们在一种媒体类型达成一致。这种媒体类型描述upstream filter将传递数据的格式。在大多数情况下,在连接的持续过程中,这个媒体类型是不变的,但是,directshow 也提供限制支持filter改变媒体类型。当一个filter转换媒体类型,叫做dynamic format change。如果你编写一个filter graph,应该意识到动态格式改变的机制。即使你的filter不需要支持这种改变,它也应该正确响应另一个filter请求一个新的格式。
对于动态格式改变Directshow 定义了几个不同的机制,依赖于filter graph 的状态和将要改变的类型。
1) 如果graph 停止的,pin 可以重新连接和重新协商媒体类型。
2) 一些filter 可以重新连接即使graph 激活的(running/paused)
另外,如果graph 激活的,但filter不支持动态pin的重新连接,有三种机制改变媒体格式。
1) QueryAccept (Downstream) 用于output pin 向downstream peer 建议改变格式,但新的格式不需要一个更大的buffer。
2) QueryAccept (Upstream)用于input pin向upstream peer建议改变格式,新的格式和原来的大小一致,也可以更大。
3) ReceiveConnection 用于output pin向downstream peer 建议改变格式,新的格式需要更大buffer
1. QueryAccept(downstream)
这个机制使一个output pin向它的同等downstream建议一个新的格式。新格式必须不请求一个更大的buffer尺寸。output pin如下操作:
1) 调用在downstream pin上的IPin::QueryAccept或者IPinConnection::DynamicQueryAccept,为了验证另一个PIN是否能接受新的媒体类型(图示,步骤A)
2) 如果第一步返回值是S_OK,这个PIN把媒体类型粘附在下一个sample。为了做到这一点,首先调用IMemAllocator::GetBuffer来获得sample(B)。然后调用IMediaSample::SetMediaType来粘附这个媒体类型到Sample(C)。通过粘附媒体类型到sample,filter显示格式已经改变,从这个sample开始。
3) pin传递sample(D)
4) 当downstream filter接收sample,它调用IMediaSample::GetMediaType来返回新的媒体类型。
 
所有的pin支持QueryAccept方法。可是,这个方法可能有轻微混淆,因为返回值S_OK不能一直保证在graph激活时可以改变格式。一些filter可能返回S_OK但如果graph激活时拒绝改变。DynamicQueryAccept方法,通过一些input pin支持,精确的定义S_OK意味着在Graph激活时pin可以改变。如果一个input pin支持IPinConnection接口,你应该调用DynamicQueryAccept而不是QueryAccept。
在多数情况下,这个机制不允许频繁改变格式,如改变位深。一种情况是用于视频解码器转换调色板。格式的基本细节是相同的,如图像尺寸和位深,但新的媒体类型有一个完全不同的调色板设置。
实现注意:
在DirectShow基类中,CBasePin::QueryAccept调用CheckMeidaType方法,在初始化PIN连接时也被调用。在一个transform filter情况下,input pin的CheckMediaType方法应该一直检查output pin是否被连接,如果这样,input媒体类型兼容于output媒体类型。因此,对于QueryAccept这个实现是合法的。如果不是,应该重载QueryAccept为了执行额外需要的检查。也注意CTransformFilter类封装这个逻辑在CheckInputType和CheckTransform方法中。CTransInPlaceFilter类,在另一方面,一直在下一个upstream/downstream调用QueryAccept。
CBaseInputPin::Receive方法检查即将到来sample的媒体类型,如果有一个,调用CheckMeidaType。可是,它不更新pin的m_mt成员,此成员保存当前媒体类型。当你的filter处理这个Sample时,你应该为这个媒体类型检查Sample。如果有新的类型,将需要存储它,不论调用在自己PIN上的SetMediaType或者直接设置m_mt值。另一方面,CVideoTransformFilter类,设计用来传输视频,当改变时存储媒体类型。
在一些情况下,只需要简单的传递QueryAccept调用downstream,然后粘附media type到output sample并且让downstream处理这个格式改变。
2. QueryAccept(upstream)
这个机制使一个input pin向它的同等upstream建议一个新的格式。downstream filter必须粘附一个媒体类型到sample,这个sample是upstream filter在它的下一个IMemAllocator::GetBuffer调用中获得的。为了做到这一点,可是,downstrema filter必须为这个连接提供一个自定义allocator。allocator必须实现一个私有方法,这个方法可以让downstream filter用来在下一个sample上设置媒体格式。
下列步骤:
1) downstream filter检查pin连接是否使用filter自定义的allocator。如果upstream filter拥有这个allocator,downstream filter不能改变格式。
2) downstream filter调用在upstream output pin上的IPin::QueryAccept(图示,步骤A)。
3) QueryAccept返回S_OK,downstream filter调用在自己allocator上的私有方法为了设置媒体类型。在私有方法中,allocator调用在下一个可用sample(B)上的IMediaSample::SetMeidaType。
4) upstream filter调用GetBuffer得到一个新的sample(C)并且调用IMediaSample::GetMediaType来得到媒体类型(D)
5) 当upstream filter传递sample,它应该让媒体类型脱离sample。这种方式,downstream filter可以确定媒体类型已经改变(E)
如果upstream filter接受格式改变,它也可以转换到原来的格式类型。
 
格式改变类型的主要例子包括DirectShow video renderers
1) 当流期间原始video renderer filter可以在RGB和YUV类型转换。当filter连接,它需要一个RGB格式来匹配当前的显示设置。这保证如果需要它可以返回到GDI。在流开始后,如果DirectDraw是可用的,video renderer请求改变格式为YUV类型。然后,如果它因为某种原因丢失DirectDraw surface,它可能会回复到RGB。
2) 新的Video Mixing Renderer(VMR) filter将连接graphics hardware支持的任何格式。包括YUV类型。可是,graph haredware可能改变DirectDraw surface内部的跨度来优化性能。VMR filter使用QueryAccept来汇报新的跨度,它指定了BITMAPINFOHEADER结构中的biWidth成员。在VIDEOINFOHEADER或 VIDEOINFOHEADER2结构中的源和目标矩形验证了视频应该解码的区域。
实现注意
对于主要的video renderer特征,未必需要写一个需要去请求upstream格式改变的filter。可是,如果编写一个video transform filter 或video renderer并且这个解码器需要传递所有QueryAccept调用upstream。当它到达时存储新的格式信息。
一个copy-transform filter(就是non-trans-in-place filter)应该实现下列行为的一种 :
1) 当它到达是传递格式改变upstream并存储新的格式信息。自己的filter必须使用一个自定义allocator以便它能粘附格式到upstream sample。
2) 在filter内部执行格式转换。这可能比传递格式改变upstream更简单。可是,它可能比让解码器filter解码到正确的格式的效率要低。
3) 最后,简单的拒绝格式改变(更多信息,引用到在DirectShow基类库中源代码CTransformInPlaceOutputPin::CheckMediaType),拒绝格式改变可能降低性能,可是,因为它阻止了video renderer使用更多有效率的格式)。
下列伪码显示如何实现一个copy-tranform filter(比CTransformFilter继承),它能在YUV和RGB输出类型中转换。此例假定filter转换自身,而不是传递格式改变upstream。
HRESULT CMyTransform::CheckInputType(const CMediaType *pmt)
{
    if (pmt is a YUV type that you support) {
        return S_OK;
    }
    else {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
}

HRESULT CMyTransform::CheckTransform(
    const CMediaType *mtIn, const CMediaType *mtOut)
{
    if (mtOut is a YUV or RGB type that you support)
    {
        if ((mtIn has the same video dimensions as mtOut) &&
            (you support the mtIn-to-mtOut transform))
        {
            return S_OK;
        }
    }
    // otherwise
    return VFW_E_TYPE_NOT_ACCEPTED;
}

// GetMediaType: Return a preferred output type.
HRESULT CMyTransform::GetMediaType(int iPosition, CMediaType *pMediaType)
{
    if (iPosition < 0) {
        return E_INVALIDARG;
    }
    switch (iPosition)
    {
    case 0:
        Copy the input type (YUV) to pMediaType
        return S_OK;
    case 1:
        Construct an RGB type that matches the input type.
        return S_OK;
    default:
        return VFW_S_NO_MORE_ITEMS;
    }
}

// SetMediaType: Override from CTransformFilter.
HRESULT CMyTransform::SetMediaType(
    PIN_DIRECTION direction, const CMediaType *pmt)
{
    // Capture this information...
    if (direction == PINDIR_OUTPUT)
    {
       m_bYuv = (pmt->subtype == MEDIASUBTYPE_UYVY);
    }
    return S_OK;
}

HRESULT CMyTransform::Transform(
    IMediaSample *pSource, IMediaSample *pDest)
{
    // Look for format changes from downstream.
    CMediaType *pMT = NULL;
    HRESULT hr = pDest->GetMediaType((AM_MEDIA_TYPE**)&pMT);
    if (hr == S_OK)
    {
        hr = m_pOutput->CheckMediaType(pMT);
        if(FAILED(hr))
        {
            DeleteMediaType(pMT);
            return E_FAIL;
        }
        // Notify our own output pin about the new type.
        m_pOutput->SetMediaType(pMT);
        DeleteMediaType(pMT);
    }
    // Process the buffers
    if (m_bYuv) {
        return ProcessFrameYUV(pSource, pDest);
    }
    else {
        return ProcessFrameRGB(pSource, pDest);
    }
}
3. ReceiveConnection
当新格式请求一个更多buffer时,这个机制使output pin请求到它downstream peer的一个格式改变。
output pin如下操作:
1) 调用在downstream input pin上的IPin::ReceiveConnection。
2) 如果ReceiveConnection成功,调用在input pin上的IMemInputPin::NotifyAllocator。
另外,output pin可能需要去调用IMemAllocator::SetProperties然后反提交和重新提交allocator为了改变buffer尺寸。确保在改变buffer尺寸前传递所有的未决sample。
如果视频尺寸改变,一些MPEG-2解码器使用这个机制在MPEG-1和MPEG-2输出之间转换。
4. 从video renderer处理格式改变
本节描述解码器filter或transform filter应该如何处理从video renderer的格式改变。
1) video renderer filter
当早期的video renderer filter连接,它请求RGB格式来匹配主要monitor的显示格式。如果directdraw不可用时它使用GDI来渲染。当回放开始时,video renderer可能转换到directdraw兼容格式。为了验证upstream是否支持新格式,video renderer调用在upstream filter的output pin上的IPin::QueryAccept。如果upstream filter接受新格式,QueryAccept方法返回S_OK。video renderer通过粘附一个用新格式的媒体类型转换到通过自己的allocator返回的下一个媒体sample(The Video Renderer switches formats by attaching a media type with the new format to the next media sample returned by its allocator)。upstream filter通过调用在每个sample上的IMediaSample::GetMediaType来查检格式改变。在流期间的任何时间video renderer可能在原始格式和新格式之间来回切换。在第一个格式改变后它不调用QueryAccept。直到upstream filter接受新格式,它必须能向前、后转换。
upstream filter可以通过从QueryAccept返回S_FALSE拒绝格式改变。在这种情况下,video renderer继续用原始格式GDI。
2) video mixing renderer filter
video mixing renderer filter(VMR-7 / vmr-9)将连接任何通过在系统上graphics hardware支持的格式。它一起使用directdraw来渲染,当upstream filter连接时它分配 内在的directdraw surface。
graphics hardware可能需要比图像宽度更大的surfac跨度,VMR通过调用QueryAccept请求一个新格式。它汇报在视频格式中BITMAPINFOHEADER中biWidth成员中的surface跨度。如果upstream filter从QueryAccept返回S_OK,VMR拒绝这个格式并且试图使用通过upstream filter建立的下一个格式来连接。VMR用新格式粘附媒体类型到第一个media sample。在第一个sample之后,格式保持不变。VMR在graph运行时不转换格式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值