C++ 媒体基础——如何改变MFT(媒体基础转换)的帧大小

C++ 媒体基础——如何改变MFT(媒体基础转换)的帧大小,c++,windows,video-processing,ms-media-foundation,C++,Windows,Video Processing,Ms Media Foundation,我正在尝试实现一个MFT,它能够旋转视频。旋转本身将在变换函数内完成。为此,我需要更改输出帧的大小,但我不知道如何做到这一点 作为起点,我使用了微软提供的MFT_灰度示例。我将此MFT作为变换节点包含在局部拓扑中 HRESULT Player::AddBranchToPartialTopology( 哲学, IMFPresentationDescriptor*pSourcePD, 德沃德伊斯特雷姆 ) { ... IMFTopologyNode pTransformNode=NULL; ...

我正在尝试实现一个MFT,它能够旋转视频。旋转本身将在变换函数内完成。为此,我需要更改输出帧的大小,但我不知道如何做到这一点

作为起点,我使用了微软提供的MFT_灰度示例。我将此MFT作为变换节点包含在局部拓扑中

HRESULT Player::AddBranchToPartialTopology(
哲学,
IMFPresentationDescriptor*pSourcePD,
德沃德伊斯特雷姆
)
{
...
IMFTopologyNode pTransformNode=NULL;
...
hr=CreateTransformNode(CLSID_GrayscaleMFT和ptTransformNode);
...
hr=pSourceNode->ConnectOutput(0,pTransferMNode,0);
hr=pTransformNode->ConnectOutput(0,pOutputNode,0);
...
}
到目前为止,该代码仍在运行。应用灰度mft并按预期工作。无论如何,我想改变这个mft来处理视频旋转。假设我想把视频旋转90度。为此,必须切换输入帧的宽度和高度。我尝试了不同的方法,但没有一种效果如预期。 基于此线程中的第一条注释,我开始更改SetOutputType的实现。我在GetOutputType中调用了GetAttributeSize来接收实际的帧大小。尝试设置新帧大小时失败(开始播放时收到hresult 0xc00d36b4(指定的数据无效、不一致或此对象不支持)

HRESULT CGrayscale::SetOutputType(
DWORD dwOutputStreamID,
IMFMediaType*pType,//可以为NULL以清除输出类型。
德沃德旗
)
{ ....
//接收pType的实际帧大小(按预期工作)
hr=MFGetAttributeSize(
pType,
MF\U MT\U框架尺寸,
&宽度,
&高度
));
...
//更改帧大小
hr=MFSetAttributeSize(
pType,
MF\U MT\U框架尺寸,
高度,
宽度
));
}
我肯定我错过了一些东西,所以任何提示都将不胜感激

提前感谢

在W8+中有一个可以进行旋转的。我自己在这方面运气不好,但大概可以让它发挥作用。我想这对你来说不是一个可行的解决方案

更有趣的情况是创建一个MFT来进行转换

事实证明,将“灰度”转换为旋转器有很多步骤

1) 正如您所猜测的,您需要影响输出类型上的帧大小。但是,更改传递给SetOutputType的类型是错误的。发送到SetOutputType的pType是客户端要求您支持的类型。将该媒体类型更改为他们要求的以外的类型,然后返回S_OK表示您支持它是没有意义的

相反,您需要更改的是从GetOutputAvailableType返回的值

2) 当计算要从GetOutputAvailableType发回的类型时,需要基于客户端发送给SetInputType的IMFMediaType,并进行一些更改。是的,您需要调整MF\U MT\U帧大小,但您可能还需要调整MF\U MT\U默认步长、MF\U MT\U几何孔径和(可能)MF\U MT\U最小显示孔径。可以想象,您可能还需要调整MF\U MT\U样本大小

3) 你们并没有说你们是否打算在开始时固定旋转量,或者在游戏过程中改变。当我写这篇文章时,我使用从imftTransform::GetAttributes返回的IMFAttributes来指定旋转。在处理每个帧之前,将读取当前值。要使这项工作正常,您需要能够从OnProcessOutput发送MF_E_TRANSFORM_STREAM_CHANGE

4) 由于懒惰,我不想知道如何旋转NV12或YUY2之类的东西。但对于RGB32,有现成的功能可以实现这一点。因此,当调用我的GetInputAvailableType时,我请求RGB32

我尝试过支持其他输入类型,如RGB24、RGB565等,但遇到了问题。当输出类型为RGB24时,MF会在下游添加另一个MFT,以将RGB24转换回更易于使用的格式(可能是RGB32)。而且MFT不支持在中流媒体中更改媒体类型。我能够通过接受输入的各种子类型来实现这一点,但总是输出RGB32,并按照指定旋转

这听起来很复杂,但大多数情况下并非如此。如果你读了代码,你可能会说“哦,我明白了。”我会给你我的源代码,但我不确定它对你有多有用。在C语言中,你问的是C++。 另一方面,我正在制作一个模板,使编写MFT更容易~十几行c#代码来创建最简单的MFT。根据VS的分析/计算代码度量(不包括模板),c#旋转MFT约为131行。我正在试用C++版本,但它仍然有点粗糙。

我忘了什么吗?可能是一堆东西。比如不要忘记为MFT生成新的Guid,而不是使用灰度。但我想我已经达到了顶点

<>编辑:现在我的C++版本的模板开始工作了,我觉得可以发布一些实际代码。这可能会使上面的一些观点更加清晰。例如,在#2中,我讨论了基于输入类型的输出类型。您可以在CreateOutputFromInput中看到这种情况。而实际的旋转代码是WriteIt()

我已经简化了代码的大小,但希望这能让你“哦,我明白了。”


我刚才碰到了这个问题。在我写下答案之前,你还在这里寻找解决方案吗?还是你自己解决了/继续前进?嗨,谢谢你的回答。虽然我实际上不再研究这个话题,但我仍然对如何解决这个问题感兴趣。因此,如果你愿意在这里写一个答案,这是非常感谢
void OnProcessSample(IMFSample *pSample, bool Discontinuity, int InputMessageNumber)
{
    HRESULT hr = S_OK;

    int i = MFGetAttributeUINT32(GetAttributes(), AttribRotate, 0);
    i &= 7;

    // Will the output use different dimensions than the input?
    bool IsOdd = (i & 1) == 1;

    // Does the current AttribRotate rotation give a different 
    // orientation than the old one?
    if (IsOdd != m_WasOdd)
    {
        // Yes, change the output type.
        OutputSample(NULL, InputMessageNumber);
        m_WasOdd = IsOdd;
    }

    // Process it.
    DoWork(pSample, (RotateFlipType)i);

    // Send the modified input sample to the output sample queue.
    OutputSample(pSample, InputMessageNumber);
}

void OnSetInputType()
{
    HRESULT hr = S_OK;

    m_imageWidthInPixels = 0;
    m_imageHeightInPixels = 0;
    m_cbImageSize = 0;
    m_lInputStride = 0;

    IMFMediaType *pmt = GetInputType();

    // type can be null to clear
    if (pmt != NULL)
    {
        hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
        ThrowExceptionForHR(hr);

        hr = pmt->GetUINT32(MF_MT_DEFAULT_STRIDE, &m_lInputStride);
        ThrowExceptionForHR(hr);

        // Calculate the image size (not including padding)
        m_cbImageSize = m_imageHeightInPixels * m_lInputStride;
    }
    else
    {
        // Since the input must be set before the output, nulling the 
        // input must also clear the output.  Note that nulling the 
        // input is only valid if we are not actively streaming.

        SetOutputType(NULL);
    }
}

IMFMediaType *CreateOutputFromInput(IMFMediaType *inType)
{
    // For some MFTs, the output type is the same as the input type.  
    // However, since we are rotating, several attributes in the 
    // media type (like frame size) must be different on our output.  
    // This routine generates the appropriate output type for the 
    // current input type, given the current state of m_WasOdd.

    IMFMediaType *pOutputType = CloneMediaType(inType);

    if (m_WasOdd)
    {
        HRESULT hr;
        UINT32 h, w;

        // Intentionally backward
        hr = MFGetAttributeSize(inType, MF_MT_FRAME_SIZE, &h, &w);
        ThrowExceptionForHR(hr);

        hr = MFSetAttributeSize(pOutputType, MF_MT_FRAME_SIZE, w, h);
        ThrowExceptionForHR(hr);

        MFVideoArea *a = GetArea(inType, MF_MT_GEOMETRIC_APERTURE);
        if (a != NULL)
        {
            a->Area.cy = h;
            a->Area.cx = w;
            SetArea(pOutputType, MF_MT_GEOMETRIC_APERTURE, a);
        }

        a = GetArea(inType, MF_MT_MINIMUM_DISPLAY_APERTURE);
        if (a != NULL)
        {
            a->Area.cy = h;
            a->Area.cx = w;
            SetArea(pOutputType, MF_MT_MINIMUM_DISPLAY_APERTURE, a);
        }

        hr = pOutputType->SetUINT32(MF_MT_DEFAULT_STRIDE, w * 4);
        ThrowExceptionForHR(hr);
    }

    return pOutputType;
}

void WriteIt(BYTE *pBuffer, RotateFlipType fm)
{
    Bitmap *v = new Bitmap((int)m_imageWidthInPixels, (int)m_imageHeightInPixels, (int)m_lInputStride, PixelFormat32bppRGB, pBuffer);
    if (v == NULL)
        throw (HRESULT)E_OUTOFMEMORY;

    try
    {
        Status s;

        s = v->RotateFlip(fm);
        if (s != Ok)
            throw (HRESULT)E_UNEXPECTED;

        Rect r;

        if (!m_WasOdd)
        {
            r.Width = (int)m_imageWidthInPixels;
            r.Height = (int)m_imageHeightInPixels;
        }
        else
        {
            r.Height = (int)m_imageWidthInPixels;
            r.Width = (int)m_imageHeightInPixels;
        }

        BitmapData bmd;
        bmd.Width = r.Width,
        bmd.Height = r.Height,
        bmd.Stride = 4*bmd.Width;
        bmd.PixelFormat = PixelFormat32bppARGB; 
        bmd.Scan0 = (VOID*)pBuffer;
        bmd.Reserved = NULL;

        s = v->LockBits(&r, ImageLockModeRead + ImageLockModeUserInputBuf, PixelFormat32bppRGB, &bmd);
        if (s != Ok)
            throw (HRESULT)E_UNEXPECTED;

        s = v->UnlockBits(&bmd);
        if (s != Ok)
            throw (HRESULT)E_UNEXPECTED;
    }
    catch(...)
    {
        delete v;
        throw;
    }

    delete v;
}