Windows Media Foundation使用IMFTransform将mp4电影帧解码为2D纹理

时间:2016-05-26 12:51:42

标签: video windows-7 mp4 directx-11 ms-media-foundation

我正在尝试使用Windows Media Foundation类解码mp4视频,并将帧转换为可由DirectX着色器用于渲染的2D纹理。我已经能够使用 MFCreateSourceReaderfromURL 阅读源流,并且能够读取其主要类型 MFMEdiaType_Video 的流媒体类型和次要类型 MFVideoFormat_H264 符合预期。

我现在需要将此格式转换为RGB格式,该格式可用于初始化D3D11_TEXTURE2D资源和资源视图,然后可以将其传递给HLSL像素着色器进行采样。我已经厌倦了使用 IMFTransform 类为我做转换,但是当我尝试将转换的输出类型设置为任何MFVideoFormat_RGB变体时,我收到错误。我也尝试在源阅读器上设置一个新的输出类型,然后只是采样,希望以正确的格式获取样本,但我又没有运气。

所以我的问题是:

  • 这种转换是否可行?

  • 这可以通过IMFTransform / SourceReader类完成,就像我上面已经累了一样,我只需要调整代码,还是需要手动进行这种类型的转换?

  • 这是将视频纹理数据输入到着色器进行采样的最佳方式,还是有一个我没想过的更容易的替代方法。

正在使用的操作系统是Windows 7,因此我无法使用SourceReaderEx或ID3D11VideoDevice接口,因为据我所知,这些解决方案似乎只在Windows 8上可用。

我们非常感谢任何正确方向的帮助/指针,如有必要,我还可以提供一些源代码。

3 个答案:

答案 0 :(得分:4)

  

这种转换是否可行?

是的,这是可能的。股票H.264 Video Decoder MFT是“Direct3D识别”,这意味着它可以利用DXVA将视频解码为Direct3D 9曲面/ Direct3D 11纹理。或者,如果硬件功能不足,则还有软件回退模式。出于性能原因,您有兴趣将输出正确地输出到纹理中(否则您将不得不自己加载这些数据并花费CPU和视频资源)。

  

这可以通过IMFTransform / SourceReader类完成,就像我上面已经累了一样,我只需要调整代码,还是需要手动进行这种类型的转换?

IMFTransform是抽象接口。它由H.264解码器(以及其他MFT)实现,您可以直接使用它,或者您可以使用更高级别的源读取器API来管理视频读取和使用此MFT解码。

也就是说,MFT和Source Reader实际上不是独占备用选项,而是更高和更低级别的API。 MFT接口由解码器提供,您负责提供H.264并排除解码输出。 Source Reader管理相同的MFT并添加文件读取功能。

Source Reader本身可用于Windows 7,BTW(即使在Vista上,与新版操作系统相比,功能设置可能有限)。

答案 1 :(得分:2)

我发现你对Media Foundation的理解有些错误。您希望从MFVideoFormat_H264获取RGB格式的图像,但不使用解码器H264。您写道“我已经厌倦了使用IMFTransform类” - IMFTransform不是类。它是Transform COM对象的接口。您必须创建COM对象Media Foundation H264解码器。 Microsoft软件H264解码器的CLSID是CLSID_CMSH264DecoderMFT。但是,从该解码器,您可以获得以下格式的输出图像: 输出类型

MFVideoFormat_I420

MFVideoFormat_IYUV

MFVideoFormat_NV12

MFVideoFormat_YUY2

MFVideoFormat_YV12

您可以从其中一个创建D3D11_TEXTURE2D。或者您可以从我的项目CaptureManager SDK中执行类似的操作:

                CComPtrCustom<IMFTransform> lColorConvert;

                if (!Result(lColorConvert.CoCreateInstance(__uuidof(CColorConvertDMO))))
                {
                    lresult = MediaFoundationManager::setInputType(
                        lColorConvert,
                        0,
                        lVideoMediaType,
                        0);

                    if (lresult)
                    {
                        break;
                    }

                    DWORD lTypeIndex = 0;

                    while (!lresult)
                    {

                        CComPtrCustom<IMFMediaType> lOutputType;

                        lresult = lColorConvert->GetOutputAvailableType(0, lTypeIndex++, &lOutputType);

                        if (!lresult)
                        {


                            lresult = MediaFoundationManager::getGUID(
                                lOutputType,
                                MF_MT_SUBTYPE,
                                lSubType);

                            if (lresult)
                            {
                                break;
                            }

                            if (lSubType == MFVideoFormat_RGB32)
                            {
                                LONG lstride = 0;

                                MediaFoundationManager::getStrideForBitmapInfoHeader(
                                    lSubType,
                                    lWidth,
                                    lstride);

                                if (lstride < 0)
                                    lstride = -lstride;

                                lBitRate = (lHight * (UINT32)lstride * 8 * lNumerator) / lDenominator;

                                lresult = MediaFoundationManager::setUINT32(
                                    lOutputType,
                                    MF_MT_AVG_BITRATE,
                                    lBitRate);

                                if (lresult)
                                {
                                    break;
                                }


                                PROPVARIANT lVarItem;

                                lresult = MediaFoundationManager::getItem(
                                    *aPtrPtrInputMediaType,
                                    MF_MT_FRAME_RATE,
                                    lVarItem);

                                if (lresult)
                                {
                                    break;
                                }

                                lresult = MediaFoundationManager::setItem(
                                    lOutputType,
                                    MF_MT_FRAME_RATE,
                                    lVarItem);

                                if (lresult)
                                {
                                    break;
                                }

                                (*aPtrPtrInputMediaType)->Release();

                                *aPtrPtrInputMediaType = lOutputType.detach();

                                break;
                            }
                        }
                    }
                }

您可以设置ColorConvertDMO,以便将H264解码器的输出格式转换为您所需的格式。

此外,您可以按链接查看代码:videoInput。此代码从网络摄像头获取实时视频并将其解码为RGB。如果您更换mp4视频文件源上的网络摄像头源,您将获得接近您需要的解决方案。

此致

答案 2 :(得分:1)

解码可以通过下一个代码执行:

                    MFT_OUTPUT_DATA_BUFFER loutputDataBuffer;

                    initOutputDataBuffer(
                        lTransform,
                        loutputDataBuffer);

                    DWORD lprocessOutputStatus = 0;

                    lresult = lTransform->ProcessOutput(
                        0,
                        1,
                        &loutputDataBuffer,
                        &lprocessOutputStatus);

                    if ((HRESULT)lresult == E_FAIL)
                    {
                        break;
                    }

函数initOutputDataBuffer分配所需的内存。该功能的示例如下:

            Result initOutputDataBuffer(IMFTransform* aPtrTransform,
            MFT_OUTPUT_DATA_BUFFER& aRefOutputBuffer)
        {
            Result lresult;

            MFT_OUTPUT_STREAM_INFO loutputStreamInfo;

            DWORD loutputStreamId = 0;

            CComPtrCustom<IMFSample> lOutputSample;

            CComPtrCustom<IMFMediaBuffer> lMediaBuffer;

            do
            {
                if (aPtrTransform == nullptr)
                {
                    lresult = E_POINTER;

                    break;
                }

                ZeroMemory(&loutputStreamInfo, sizeof(loutputStreamInfo));

                ZeroMemory(&aRefOutputBuffer, sizeof(aRefOutputBuffer));

                lresult = aPtrTransform->GetOutputStreamInfo(loutputStreamId, &loutputStreamInfo);

                if (lresult)
                {
                    break;
                }

                if ((loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0 &&
                    (loutputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) == 0)
                {
                    lresult = MFCreateSample(&lOutputSample);

                    if (lresult)
                    {
                        break;
                    }

                    lresult = MFCreateMemoryBuffer(loutputStreamInfo.cbSize, &lMediaBuffer);

                    if (lresult)
                    {
                        break;
                    }

                    lresult = lOutputSample->AddBuffer(lMediaBuffer);

                    if (lresult)
                    {
                        break;
                    }

                    aRefOutputBuffer.pSample = lOutputSample.Detach();
                }
                else
                {
                    lresult = S_OK;
                }

                aRefOutputBuffer.dwStreamID = loutputStreamId;
            } while (false);

            return lresult;
        }

它需要通过IMFTransform的GetOutputStreamInfo方法获取有关输出样本的信息。 MFT_OUTPUT_STREAM_INFO包含有关输出媒体样本所需内存大小的信息 - cbSize。它需要分配具有该大小的内存,将其添加到MediaSample并将其附加到MFT_OUTPUT_DATA_BUFFER。

因此,您会发现通过直接调用MediaFoundation函数来编写和解码视频的代码可能很困难,并且需要对它有重要的了解。从你的任务描述我看到你只需解码视频并呈现它。我建议您尝试使用Media Foundation Session功能。它由Microsoft的工程师开发,并已包含使用所需编码器和优化的算法。在项目videoInput中,Media Foundation Session用于为媒体源找到合适的解码器,该解码器是为网络摄像头创建的,并以非压缩格式抓取帧。它已经完成了所需的处理。您只需从视频文件中的Media Source上的Web摄像头替换Media Source。通过直接调用IMFTransform进行解码可以更轻松地编写代码,并允许简化许多问题(例如 - 稳定帧速率。如果代码在解码后立即渲染图像然后解码新帧,那么它可以渲染1分钟视频在几秒钟内剪辑,或者如果视频和其他内容的渲染可能需要多个帧持续时间,则视频可以在&#34;慢动作&#34;样式和1分钟视频剪辑的渲染中呈现2,3或者5分钟。 我不知道您需要解码视频的项目,但是您应该有充分的理由使用直接调用Media Foundation功能和接口的代码。

问候。