Я собираю десктоп с помощью DesktopDuplication API и преобразую сэмплы из RGBA в NV12 в графическом процессоре и передаю их на аппаратное обеспечение MediaFoundation H264 MFT. Это прекрасно работает с графикой Nvidia, а также с программными кодировщиками, но дает сбой, когда доступно только графическое оборудование Intel MFT. Код прекрасно работает на том же графическом компьютере Intel, если я откажусь от Software MFT. Я также позаботился о том, чтобы кодирование выполнялось аппаратно на графических машинах Nvidia.

На графике Intel MFT возвращает MEError ( «Неопределенная ошибка» ), которая происходит сразу после подачи первого образца, а последующие вызовы ProcessInput (когда генератор событий запускает METransformNeedInput) возвращает "The вызываемый абонент в настоящее время не принимает дальнейший ввод ". Редко, когда MFT потребляет еще несколько выборок, прежде чем вернуть эти ошибки. Такое поведение сбивает с толку, я загружаю образец только тогда, когда генератор событий запускает METransformNeedInput асинхронно через IMFAsyncCallback, а также правильно проверяю, срабатывает ли METransformHaveOutput, как только образец загружен. Это действительно сбивает меня с толку, когда та же асинхронная логика отлично работает с аппаратными кодировщиками MFT Nvidia и программными кодировщиками Microsoft.

Существует также похожий нерешенный вопрос в Сам форум Intel. Мой код похож на тот, который упоминается в теме Intel, за исключением того факта, что я также устанавливаю диспетчер устройств d3d для кодировщика, как показано ниже.

И есть еще три других потока переполнения стека, сообщающих об аналогичной проблеме без решения (MFTransform encoder -> ProcessInput возвращает E_FAIL & Как создать IMFSample из текстуры D11 для Intel MFT кодировщик и Асинхронный MFT не отправляет событие MFTransformHaveOutput ( Intel Hardware MJPEG Decoder MFT)). Я перепробовал все возможные варианты, но ничего не изменилось.

код преобразователя цвета взят из Intel Media образцы SDK. Я также загрузил свой полный код здесь.

Способ установки менеджера d3d:

void SetD3dManager() {

    HRESULT hr = S_OK;

    if (!deviceManager) {

        // Create device manager
        hr = MFCreateDXGIDeviceManager(&resetToken, &deviceManager);
    }

    if (SUCCEEDED(hr)) 
    {
        if (!pD3dDevice) {

            pD3dDevice = GetDeviceDirect3D(0);
        }
    }

    if (pD3dDevice) {

        // NOTE: Getting ready for multi-threaded operation
        const CComQIPtr<ID3D10Multithread> pMultithread = pD3dDevice;
        pMultithread->SetMultithreadProtected(TRUE);

        hr = deviceManager->ResetDevice(pD3dDevice, resetToken);
        CHECK_HR(_pTransform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, reinterpret_cast<ULONG_PTR>(deviceManager.p)), "Failed to set device manager.");
    }
    else {
        cout << "Failed to get d3d device";
    }
}

Getd3ddevice :

CComPtr<ID3D11Device> GetDeviceDirect3D(UINT idxVideoAdapter)
{
    // Create DXGI factory:
    CComPtr<IDXGIFactory1> dxgiFactory;
    DXGI_ADAPTER_DESC1 dxgiAdapterDesc;

    // Direct3D feature level codes and names:

    struct KeyValPair { int code; const char* name; };

    const KeyValPair d3dFLevelNames[] =
    {
        KeyValPair{ D3D_FEATURE_LEVEL_9_1, "Direct3D 9.1" },
        KeyValPair{ D3D_FEATURE_LEVEL_9_2, "Direct3D 9.2" },
        KeyValPair{ D3D_FEATURE_LEVEL_9_3, "Direct3D 9.3" },
        KeyValPair{ D3D_FEATURE_LEVEL_10_0, "Direct3D 10.0" },
        KeyValPair{ D3D_FEATURE_LEVEL_10_1, "Direct3D 10.1" },
        KeyValPair{ D3D_FEATURE_LEVEL_11_0, "Direct3D 11.0" },
        KeyValPair{ D3D_FEATURE_LEVEL_11_1, "Direct3D 11.1" },
    };

    // Feature levels for Direct3D support
    const D3D_FEATURE_LEVEL d3dFeatureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1,
    };

    constexpr auto nFeatLevels = static_cast<UINT> ((sizeof d3dFeatureLevels) / sizeof(D3D_FEATURE_LEVEL));

    CComPtr<IDXGIAdapter1> dxgiAdapter;
    D3D_FEATURE_LEVEL featLevelCodeSuccess;
    CComPtr<ID3D11Device> d3dDx11Device;

    std::wstring_convert<std::codecvt_utf8<wchar_t>> transcoder;

    HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
    CHECK_HR(hr, "Failed to create DXGI factory");

    // Get a video adapter:
    dxgiFactory->EnumAdapters1(idxVideoAdapter, &dxgiAdapter);

    // Get video adapter description:
    dxgiAdapter->GetDesc1(&dxgiAdapterDesc);

    CHECK_HR(hr, "Failed to retrieve DXGI video adapter description");

    std::cout << "Selected DXGI video adapter is \'"
        << transcoder.to_bytes(dxgiAdapterDesc.Description) << '\'' << std::endl;

    // Create Direct3D device:
    hr = D3D11CreateDevice(
        dxgiAdapter,
        D3D_DRIVER_TYPE_UNKNOWN,
        nullptr,
        (0 * D3D11_CREATE_DEVICE_SINGLETHREADED) | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
        d3dFeatureLevels,
        nFeatLevels,
        D3D11_SDK_VERSION,
        &d3dDx11Device,
        &featLevelCodeSuccess,
        nullptr
    );

    // Might have failed for lack of Direct3D 11.1 runtime:
    if (hr == E_INVALIDARG)
    {
        // Try again without Direct3D 11.1:
        hr = D3D11CreateDevice(
            dxgiAdapter,
            D3D_DRIVER_TYPE_UNKNOWN,
            nullptr,
            (0 * D3D11_CREATE_DEVICE_SINGLETHREADED) | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
            d3dFeatureLevels + 1,
            nFeatLevels - 1,
            D3D11_SDK_VERSION,
            &d3dDx11Device,
            &featLevelCodeSuccess,
            nullptr
        );
    }

    // Get name of Direct3D feature level that succeeded upon device creation:
    std::cout << "Hardware device supports " << std::find_if(
        d3dFLevelNames,
        d3dFLevelNames + nFeatLevels,
        [featLevelCodeSuccess](const KeyValPair& entry)
        {
            return entry.code == featLevelCodeSuccess;
        }
    )->name << std::endl;

done:

    return d3dDx11Device;
}

Реализация асинхронного обратного вызова:

struct EncoderCallbacks : IMFAsyncCallback
{
    EncoderCallbacks(IMFTransform* encoder)
    {
        TickEvent = CreateEvent(0, FALSE, FALSE, 0);
        _pEncoder = encoder;
    }

    ~EncoderCallbacks()
    {
        eventGen = nullptr;
        CloseHandle(TickEvent);
    }

    bool Initialize() {

        _pEncoder->QueryInterface(IID_PPV_ARGS(&eventGen));

        if (eventGen) {

            eventGen->BeginGetEvent(this, 0);
            return true;
        }

        return false;
    }

    // dummy IUnknown impl
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
    virtual ULONG STDMETHODCALLTYPE AddRef(void) override { return 1; }
    virtual ULONG STDMETHODCALLTYPE Release(void) override { return 1; }

    virtual HRESULT STDMETHODCALLTYPE GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) override
    {
        // we return immediately and don't do anything except signaling another thread
        *pdwFlags = MFASYNC_SIGNAL_CALLBACK;
        *pdwQueue = MFASYNC_CALLBACK_QUEUE_IO;
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult* pAsyncResult) override
    {
        IMFMediaEvent* event = 0;
        eventGen->EndGetEvent(pAsyncResult, &event);
        if (event)
        {
            MediaEventType type;
            event->GetType(&type);
            switch (type)
            {
            case METransformNeedInput: InterlockedIncrement(&NeedsInput); break;
            case METransformHaveOutput: InterlockedIncrement(&HasOutput); break;
            }
            event->Release();
            SetEvent(TickEvent);
        }

        eventGen->BeginGetEvent(this, 0);
        return S_OK;
    }

    CComQIPtr<IMFMediaEventGenerator> eventGen = nullptr;
    HANDLE TickEvent;
    IMFTransform* _pEncoder = nullptr;

    unsigned int NeedsInput = 0;
    unsigned int HasOutput = 0;
};

Сгенерировать образец метода:

bool GenerateSampleAsync() {

    DWORD processOutputStatus = 0;
    HRESULT mftProcessOutput = S_OK;
    bool frameSent = false;

    // Create sample
    CComPtr<IMFSample> currentVideoSample = nullptr;

    MFT_OUTPUT_STREAM_INFO StreamInfo;

    // wait for any callback to come in
    WaitForSingleObject(_pEventCallback->TickEvent, INFINITE);

    while (_pEventCallback->NeedsInput) {

        if (!currentVideoSample) {

            (pDesktopDuplication)->releaseBuffer();
            (pDesktopDuplication)->cleanUpCurrentFrameObjects();

            bool bTimeout = false;

            if (pDesktopDuplication->GetCurrentFrameAsVideoSample((void**)& currentVideoSample, waitTime, bTimeout, deviceRect, deviceRect.Width(), deviceRect.Height())) {

                prevVideoSample = currentVideoSample;
            }
            // Feed the previous sample to the encoder in case of no update in display
            else {
                currentVideoSample = prevVideoSample;
            }
        }

        if (currentVideoSample)
        {
            InterlockedDecrement(&_pEventCallback->NeedsInput);
            _frameCount++;

            CHECK_HR(currentVideoSample->SetSampleTime(mTimeStamp), "Error setting the video sample time.");
            CHECK_HR(currentVideoSample->SetSampleDuration(VIDEO_FRAME_DURATION), "Error getting video sample duration.");

            CHECK_HR(_pTransform->ProcessInput(inputStreamID, currentVideoSample, 0), "The resampler H264 ProcessInput call failed.");

            mTimeStamp += VIDEO_FRAME_DURATION;
        }
    }

    while (_pEventCallback->HasOutput) {

        CComPtr<IMFSample> mftOutSample = nullptr;
        CComPtr<IMFMediaBuffer> pOutMediaBuffer = nullptr;

        InterlockedDecrement(&_pEventCallback->HasOutput);

        CHECK_HR(_pTransform->GetOutputStreamInfo(outputStreamID, &StreamInfo), "Failed to get output stream info from H264 MFT.");

        CHECK_HR(MFCreateSample(&mftOutSample), "Failed to create MF sample.");
        CHECK_HR(MFCreateMemoryBuffer(StreamInfo.cbSize, &pOutMediaBuffer), "Failed to create memory buffer.");
        CHECK_HR(mftOutSample->AddBuffer(pOutMediaBuffer), "Failed to add sample to buffer.");

        MFT_OUTPUT_DATA_BUFFER _outputDataBuffer;
        memset(&_outputDataBuffer, 0, sizeof _outputDataBuffer);
        _outputDataBuffer.dwStreamID = outputStreamID;
        _outputDataBuffer.dwStatus = 0;
        _outputDataBuffer.pEvents = nullptr;
        _outputDataBuffer.pSample = mftOutSample;

        mftProcessOutput = _pTransform->ProcessOutput(0, 1, &_outputDataBuffer, &processOutputStatus);

        if (mftProcessOutput != MF_E_TRANSFORM_NEED_MORE_INPUT)
        {
            if (_outputDataBuffer.pSample) {

                CComPtr<IMFMediaBuffer> buf = NULL;
                DWORD bufLength;
                CHECK_HR(_outputDataBuffer.pSample->ConvertToContiguousBuffer(&buf), "ConvertToContiguousBuffer failed.");

                if (buf) {

                    CHECK_HR(buf->GetCurrentLength(&bufLength), "Get buffer length failed.");
                    BYTE* rawBuffer = NULL;

                    fFrameSize = bufLength;
                    fDurationInMicroseconds = 0;
                    gettimeofday(&fPresentationTime, NULL);

                    buf->Lock(&rawBuffer, NULL, NULL);
                    memmove(fTo, rawBuffer, fFrameSize > fMaxSize ? fMaxSize : fFrameSize);

                    bytesTransfered += bufLength;

                    FramedSource::afterGetting(this);

                    buf->Unlock();

                    frameSent = true;
                }
            }

            if (_outputDataBuffer.pEvents)
                _outputDataBuffer.pEvents->Release();
        }
        else if (MF_E_TRANSFORM_STREAM_CHANGE == mftProcessOutput) {

            // some encoders want to renegotiate the output format. 
            if (_outputDataBuffer.dwStatus & MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE)
            {
                CComPtr<IMFMediaType> pNewOutputMediaType = nullptr;
                HRESULT res = _pTransform->GetOutputAvailableType(outputStreamID, 1, &pNewOutputMediaType);

                res = _pTransform->SetOutputType(0, pNewOutputMediaType, 0);//setting the type again
                CHECK_HR(res, "Failed to set output type during stream change");
            }
        }
        else {
            HandleFailure();
        }
    }

    return frameSent;
}

Создать образец видео и преобразование цветов:

bool GetCurrentFrameAsVideoSample(void **videoSample, int waitTime, bool &isTimeout, CRect &deviceRect, int surfaceWidth, int surfaceHeight)
{

FRAME_DATA currentFrameData;

m_LastErrorCode = m_DuplicationManager.GetFrame(&currentFrameData, waitTime, &isTimeout);

if (!isTimeout && SUCCEEDED(m_LastErrorCode)) {

    m_CurrentFrameTexture = currentFrameData.Frame;

    if (!pDstTexture) {

        D3D11_TEXTURE2D_DESC desc;
        ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));

        desc.Format = DXGI_FORMAT_NV12;
        desc.Width = surfaceWidth;
        desc.Height = surfaceHeight;
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.SampleDesc.Count = 1;
        desc.CPUAccessFlags = 0;
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.BindFlags = D3D11_BIND_RENDER_TARGET;

        m_LastErrorCode = m_Id3d11Device->CreateTexture2D(&desc, NULL, &pDstTexture);
    }

    if (m_CurrentFrameTexture && pDstTexture) {

        // Copy diff area texels to new temp texture
        //m_Id3d11DeviceContext->CopySubresourceRegion(pNewTexture, D3D11CalcSubresource(0, 0, 1), 0, 0, 0, m_CurrentFrameTexture, 0, NULL);

        HRESULT hr = pColorConv->Convert(m_CurrentFrameTexture, pDstTexture);

        if (SUCCEEDED(hr)) { 

            CComPtr<IMFMediaBuffer> pMediaBuffer = nullptr;

            MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pDstTexture, 0, FALSE, (IMFMediaBuffer**)&pMediaBuffer);

            if (pMediaBuffer) {

                CComPtr<IMF2DBuffer> p2DBuffer = NULL;
                DWORD length = 0;
                (((IMFMediaBuffer*)pMediaBuffer))->QueryInterface(__uuidof(IMF2DBuffer), reinterpret_cast<void**>(&p2DBuffer));
                p2DBuffer->GetContiguousLength(&length);
                (((IMFMediaBuffer*)pMediaBuffer))->SetCurrentLength(length);

                //MFCreateVideoSampleFromSurface(NULL, (IMFSample**)videoSample);
                MFCreateSample((IMFSample * *)videoSample);

                if (videoSample) {

                    (*((IMFSample **)videoSample))->AddBuffer((((IMFMediaBuffer*)pMediaBuffer)));
                }

                return true;
            }
        }
    }
}

return false;
}

Графический драйвер Intel в машине уже обновлен.

enter image description here enter image description here enter image description here

Только событие TransformNeedInput вызывается все время, но кодер жалуется, что больше не может принимать ввод. Событие TransformHaveOutput никогда не вызывалось.

enter image description here

Подобные проблемы сообщаются на форумах Intel и MSDN: 1) https://software.intel.com/ ru-ru / forum / intel-media-sdk / topic / 607189 2) https://social.msdn.microsoft.com/Forums/SECURITY/en-US/fe051dd5-b522-4e4b- 9cbb-2c06a5450e40 / imfsinkwriter-merit-validation-failed-for-mft-intel-quick-sync-video-h264-encoder-mft? Forum = mediafoundationdevelopment

< Сильный > Update : Я попытался смоделировать только источник входного сигнала (путем программного создания образца прямоугольника анимации NV12), оставив все остальное нетронутым. На этот раз кодировщик intel ни на что не жалуется, у меня даже есть выходные образцы. За исключением того факта, что выходное видео кодера Intel искажено, тогда как кодер Nvidia работает отлично.

Кроме того, я все еще получаю ошибку ProcessInput для моего исходного источника NV12 с кодировщиком Intel. У меня нет проблем с Nvidia MFT и программными кодировщиками.

Вывод аппаратного обеспечения Intel MFT: (посмотрите на вывод кодера Nvidia) введите описание изображения здесь

Вывод аппаратного обеспечения MFT от Nvidia . введите описание изображения здесь

Статистика использования графики Nvidia: введите описание изображения здесь

Статистика использования графики Intel (я не понимаю, почему движок графического процессора отображается как декодирование видео): введите описание изображения здесь

9
Ram 9 Ноя 2019 в 16:52

2 ответа

Лучший ответ

Как упоминалось в посте, ошибка MEError («Unspecified error») была возвращена генератором событий Transform сразу после подачи первой выборки ввода на аппаратном обеспечении Intel, а дальнейшие вызовы только что вернули «Transform Need more input», но вывод не был получен , Тот же код работал нормально на машинах Nvidia, хотя. После долгих экспериментов и исследований я обнаружил, что создаю слишком много экземпляров D3d11Device. В моем случае я создал от 2 до 3 устройств для захвата, преобразования цветов и аппаратного кодирования соответственно. Принимая во внимание, что я мог бы просто повторно использовать один экземпляр D3dDevice. Однако создание нескольких экземпляров D3d11Device может работать на компьютерах высокого уровня. Это нигде не задокументировано. Я не смог найти даже подсказки о причинах ошибки "MEError". Это нигде не упоминается. Было много потоков StackOverflow, подобных тем, которые оставались без ответа, даже сотрудники Microsoft не могли указать на проблему, учитывая полный исходный код.

Повторное использование экземпляра D3D11Device решило проблему. Публикация этого решения может быть полезной для людей, которые сталкиваются с той же проблемой, что и я.

1
Ram 25 Ноя 2019 в 08:16

Я посмотрел на ваш код.

Согласно вашему сообщению, я подозреваю, что проблема с видеопроцессором Intel.

Моя ОС Win7, поэтому я решил проверить поведение видеопроцессора с помощью D3D9Device на моей карте Nvidia, а затем на Intel HD Graphics 4000.

Я полагаю, что возможности видеопроцессора будут работать для D3D9Device так же, как и для D3D11Device. Конечно надо будет проверить.

Поэтому я сделал эту программу для проверки: https://github.com/mofo7777/DirectXVideoScreen (см. D3D9VideoProcessor суб - проект)

Кажется, вы не проверяете достаточно о возможностях видеопроцессора.

С помощью IDXVAHD_Device :: GetVideoProcessorDeviceCaps вот что я проверяю:

DXVAHD_VPDEVCAPS.MaxInputStreams> 0

DXVAHD_VPDEVCAPS.VideoProcessorCount> 0

DXVAHD_VPDEVCAPS.OutputFormatCount> 0

DXVAHD_VPDEVCAPS.InputFormatCount> 0

DXVAHD_VPDEVCAPS.InputPool == D3DPOOL_DEFAULT

Я также проверяю формат ввода и вывода, поддерживаемый с IDXVAHD_Device :: GetVideoProcessorOutputFormats и IDXVAHD_Device :: GetVideoProcessorInputFormats.

Именно здесь я обнаружил разницу между Nvidia GPU и Intel GPU.

NVIDIA: 4 формата вывода

  • D3DFMT_A8R8G8B8
  • D3DFMT_X8R8G8B8
  • D3DFMT_YUY2
  • D3DFMT_NV12

INTEL: 3 формата вывода

  • D3DFMT_A8R8G8B8
  • D3DFMT_X8R8G8B8
  • D3DFMT_YUY2

В Intel HD Graphics 4000 нет поддержки выходного формата NV12.

Также, чтобы программа работала правильно, мне нужно настроить состояние потока перед использованием VideoProcessBltHD:

  • DXVAHD_STREAM_STATE_D3DFORMAT
  • DXVAHD_STREAM_STATE_FRAME_FORMAT
  • DXVAHD_STREAM_STATE_INPUT_COLOR_SPACE
  • DXVAHD_STREAM_STATE_SOURCE_RECT
  • DXVAHD_STREAM_STATE_DESTINATION_RECT

Для D3D11:

ID3D11VideoProcessorEnumerator :: GetVideoProcessorCaps == IDXVAHD_Device :: GetVideoProcessorDeviceCaps

(D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT) ID3D11VideoProcessorEnumerator :: CheckVideoProcessorFormat == IDXVAHD_Device :: GetVideoProcessorOutputFormats

(D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT) ID3D11VideoProcessorEnumerator :: CheckVideoProcessorFormat == IDXVAHD_Device :: GetVideoProcessorInputFormats

ID3D11VideoContext :: (...) == IDXVAHD_VideoProcessor :: SetVideoProcessStreamState

Не могли бы вы сначала проверить возможности видеопроцессора вашего графического процессора. Вы видите ту же разницу, что и я?

Это первое, что нам нужно знать, и, похоже, ваша программа не проверяет это, исходя из того, что я видел в вашем проекте github.

2
mofo77 13 Ноя 2019 в 21:50