Я собираю десктоп с помощью DesktopDuplication API и преобразую сэмплы из RGBA в NV12 в графическом процессоре и передаю их на аппаратное обеспечение MediaFoundation H264 MFT. Это прекрасно работает с графикой Nvidia, а также с программными кодировщиками, но дает сбой, когда доступно только графическое оборудование Intel MFT. Код прекрасно работает на том же графическом компьютере Intel, если я откажусь от Software MFT. Я также гарантировал, что кодирование на самом деле выполняется аппаратно на графических машинах Nvidia.
На графике Intel MFT возвращает MEError ( « Неуказанная ошибка» ), которая происходит только после подачи первого образца, а последующие вызовы ProcessInput (когда генератор событий вызывает METransformNeedInput) возвращают «Вызываемый в настоящее время не принимает дальнейшие входные данные» . Редко, когда MFT потребляет еще несколько сэмплов перед возвратом этих ошибок. Такое поведение сбивает с толку, я подаю образец только тогда, когда генератор событий запускает METransformNeedInput асинхронно через IMFAsyncCallback, а также проверяет, правильно ли запущен METransformHaveOutput, как только образец подан. Это действительно сбивает с толку меня, когда та же асинхронная логика прекрасно работает с аппаратными кодировщиками MFT и Microsoft от Nvidia.
Существует также аналогичный нерешенный вопрос на самом форуме Intel. Мой код похож на тот, который упоминается в теме Intel, за исключением того факта, что я также настраиваю диспетчер устройств d3d на кодировщик, как показано ниже.
И есть три других потока переполнения стека, сообщающих о подобной проблеме без решения ( MFTransform encoder-> ProcessInput возвращает E_FAIL & Как создать IMFSample из текстуры D11 для Intel MFT encoder & Асинхронный MFT не отправляет событие MFTransformHaveOutput (аппаратное обеспечение Intel 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(¤tFrameData, 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 в машине уже обновлен.
Только событие TransformNeedInput запускается все время, но кодер жалуется, что больше не может принимать ввод. Событие TransformHaveOutput никогда не вызывалось.
О подобных проблемах сообщалось на форумах Intel и MSDN: 1) https://software.intel.com/en-us/forums/intel-media-sdk/topic/607189 2) https://social.msdn.microsoft.com/ форум / БЕЗОПАСНОСТЬ / EN-US / fe051dd5-b522-4e4b-9cbb-2c06a5450e40 / imfsinkwriter-заслуги валидация-неудавшаяся для-MFT-Intel-быстрая синхронизация видео-h264-кодер-MFT? форум = mediafoundationdevelopment
Обновление: я попытался смоделировать только источник входного сигнала (путем программного создания образца анимирующего прямоугольника NV12), оставив все остальное нетронутым. На этот раз кодировщик intel ни на что не жалуется, у меня даже есть выходные образцы. За исключением того факта, что выходное видео кодера Intel искажено, тогда как кодер Nvidia работает отлично.
Кроме того, я все еще получаю ошибку ProcessInput для моего исходного источника NV12 с кодировщиком Intel. У меня нет проблем с Nvidia MFT и программными кодировщиками.
Вывод аппаратного обеспечения Intel MFT: (Пожалуйста, посмотрите на вывод кодера Nvidia)
Выход аппаратного обеспечения Nvidia MFT:
Статистика использования графики Nvidia:
Статистика использования графики Intel (я не понимаю, почему движок графического процессора отображается как декодирование видео):
ProcessInput
.Ответы:
Я посмотрел на ваш код.
Согласно вашему сообщению, я подозреваю, что проблема с видеопроцессором 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 формата вывода
INTEL: 3 формата вывода
На Intel HD Graphics 4000 отсутствует поддержка выходного формата NV12.
Также, чтобы программа работала правильно, мне нужно настроить состояние потока перед использованием VideoProcessBltHD:
Для 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.
источник
Как упоминалось в посте, ошибка MEError («Unspecified error») была возвращена генератором событий Transform сразу после подачи первой выборки ввода на аппаратном обеспечении Intel, а дальнейшие вызовы только что вернули «Transform Need more input», но вывод не был получен , Тот же код работал нормально на машинах Nvidia. После долгих экспериментов и исследований я обнаружил, что создаю слишком много экземпляров D3d11Device. В моем случае я создал 2-3 устройства для захвата, преобразования цветов и аппаратного кодирования соответственно. Принимая во внимание, что я мог бы просто повторно использовать один экземпляр D3dDevice. Однако создание нескольких экземпляров D3d11Device может работать на компьютерах высокого уровня. Это нигде не задокументировано. Я не смог найти даже подсказки о причинах ошибки "MEError". Это нигде не упоминается.
Повторное использование экземпляра D3D11Device решило проблему. Публикация этого решения может быть полезной для людей, которые сталкиваются с той же проблемой, что и я.
источник