我正在尝试实现能够旋转视频的MFT。旋转本身将在转换函数内完成。为此,我需要更改输出帧的大小,但是我不知道该怎么做。

首先,我使用了Microsoft提供的MFT_Grayscale示例。我将此MFT包含在部分拓扑中作为转换节点

HRESULT Player::AddBranchToPartialTopology(
    IMFTopology *pTopology,
    IMFPresentationDescriptor *pSourcePD,
    DWORD iStream
    )
{
    ...
    IMFTopologyNode pTransformNode = NULL;
    ...
    hr = CreateTransformNode(CLSID_GrayscaleMFT, &pTransformNode);
    ...
    hr = pSourceNode->ConnectOutput(0, pTransformNode, 0);
    hr = pTransformNode->ConnectOutput(0, pOutputNode, 0);
    ...
}

到目前为止,该代码一直有效。灰度mft已应用并按预期工作。无论如何,我想更改此MFT以处理视频旋转。因此,假设我想将视频旋转90度。为此,必须切换输入框的宽度和高度。我尝试了不同的方法,但没有一个能像预期的那样工作。
基于该线程How to change Media Foundation Transform output frame(video) size?中的第一个注释,我开始更改SetOutputType的实现。我在GetOutputType内调用了GetAttributeSize来接收实际的frame_size。当我尝试设置新的frame_size时,它失败(当开始播放时,我收到hresult 0xc00d36b4(指定的数据无效,不一致或此对象不支持)

HRESULT CGrayscale::SetOutputType(
    DWORD           dwOutputStreamID,
    IMFMediaType    *pType, // Can be NULL to clear the output type.
    DWORD           dwFlags
    )
{ ....
    //Receive the actual frame_size of pType (works as expected)
    hr = MFGetAttributeSize(
    pType,
    MF_MT_FRAME_SIZE,
    &width,
    &height
    ));
    ...
    //change the framesize
    hr = MFSetAttributeSize(
    pType,
    MF_MT_FRAME_SIZE,
    height,
    width
    ));
}

我确定我在这里想念任何东西,因此任何提示将不胜感激。

提前致谢

最佳答案

W8 +中有一个transform可以旋转。我本人并没有很幸运,但是大概可以使它工作了。我将假定这对您来说不是一个可行的解决方案。

更有趣的情况是创建MFT进行转换。

事实证明,有许多步骤可以将“灰度”转换为旋转器。

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

相反,您需要更改的是从GetOutputAvailableType发送回的值。

2)在计算要从GetOutputAvailableType发送回的类型时,您需要将其基于客户端发送给SetInputType的IMFMediaType进行一些更改。是的,您想调整MF_MT_FRAME_SIZE,但您可能还需要调整MF_MT_DEFAULT_STRIDE,MF_MT_GEOMETRIC_APERTURE和(可能)MF_MT_MINIMUM_DISPLAY_APERTURE。可以想象,您可能还需要调整MF_MT_SAMPLE_SIZE。

3)您没有说是要在流开始时固定旋转量,还是要在播放过程中改变旋转量。当我写这篇文章时,我使用了从IMFTransform::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。 C#旋转MFT为131行,由VS的“分析/计算”代码指标(不包括模板)计算得出。我正在尝试使用C++版本,但是仍然有些粗糙。

我忘记了什么吗?大概是一堆东西。就像别忘了为您的MFT生成新的Guid,而不是使用Grayscale的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;
}

关于c++ - Media Foundation-如何在MFT中更改帧大小(Media Foundation Transform),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27981951/

10-09 03:52