Ich erfasse den Desktop mithilfe der DesktopDuplication-API, konvertiere die Beispiele von RGBA in NV12 in der GPU und speise sie in die MediaFoundation-Hardware H264 MFT ein. Dies funktioniert gut mit Nvidia-Grafiken und auch mit Software-Encodern, schlägt jedoch fehl, wenn nur Intel-Grafikhardware MFT verfügbar ist. Der Code funktioniert auf derselben Intel-Grafikmaschine einwandfrei, wenn ich auf Software MFT zurückgreife. Ich habe auch sichergestellt, dass die Codierung tatsächlich in Hardware auf Nvidia-Grafikmaschinen erfolgt.
Bei Intel-Grafiken gibt MFT MEError ( "Nicht spezifizierter Fehler" ) zurück, was unmittelbar nach dem Einspeisen des ersten Samples auftritt, und nachfolgende Aufrufe von ProcessInput (wenn der Ereignisgenerator METransformNeedInput auslöst) geben "Der Angerufene akzeptiert derzeit keine weiteren Eingaben" zurück . Es ist selten, dass MFT einige weitere Proben verbraucht, bevor diese Fehler zurückgegeben werden. Dieses Verhalten ist verwirrend. Ich füttere ein Sample nur, wenn der Ereignisgenerator METransformNeedInput asynchron über IMFAsyncCallback auslöst, und überprüfe auch ordnungsgemäß, ob METransformHaveOutput ausgelöst wird, sobald ein Sample gespeist wird. Das verwirrt mich wirklich, wenn dieselbe asynchrone Logik mit Nvidia-Hardware-MFT- und Microsoft-Software-Encodern einwandfrei funktioniert.
Es gibt auch eine ähnliche ungelöste Frage im Intel-Forum. Mein Code ähnelt dem im Intel-Thread erwähnten, außer dass ich auch den d3d-Geräte-Manager wie unten auf den Encoder setze.
Und, gibt es drei weitere Stapelüberlauf - Threads ein ähnliches Problem ohne Lösung gegeben Berichterstattung ( MFTransform Encoder-> Process E_Fail & Wie IMFSample von D11 Textur für Intel MFT - Encoder erstellen und Asynchronous MFT nicht MFTransformHaveOutput Ereignis (Intel Hardware MJPEG Decoder zu senden MFT) ). Ich habe jede mögliche Option ausprobiert, ohne dies zu verbessern.
Der Farbkonvertercode stammt aus Intel Media SDK-Beispielen. Ich habe auch meinen kompletten Code hochgeladen hier .
Methode zum Festlegen des d3d-Managers:
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;
}
Asynchrone Rückrufimplementierung:
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;
};
Beispielmethode generieren:
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;
}
Videobeispiel und Farbkonvertierung erstellen:
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;
}
Der Intel-Grafiktreiber im Computer ist bereits auf dem neuesten Stand.
Nur das TransformNeedInput-Ereignis wird ständig ausgelöst, der Encoder beschwert sich jedoch, dass er keine weiteren Eingaben akzeptieren kann. Das TransformHaveOutput-Ereignis wurde nie ausgelöst.
Ähnliche Probleme wurden in Intel & msdn-Foren gemeldet: 1) https://software.intel.com/en-us/forums/intel-media-sdk/topic/607189 2) https://social.msdn.microsoft.com/ Foren / SICHERHEIT / de-US / fe051dd5-b522-4e4b-9cbb-2c06a5450e40 / Imfsinkwriter-Merit-Validierung-fehlgeschlagen-für-Mft-Intel-Quick-Sync-Video-H264-Encoder-Mft? Forum = Mediafoundation-Entwicklung
Update: Ich habe versucht, nur die Eingabequelle zu verspotten (indem ich programmgesteuert ein animierendes Rechteck-NV12-Beispiel erstellt habe), wobei alles andere unberührt blieb. Diesmal beschwert sich der Intel-Encoder nicht, ich habe sogar Ausgangsbeispiele. Abgesehen von der Tatsache, dass das Ausgangsvideo des Intel-Encoders verzerrt ist, während der Nvidia-Encoder einwandfrei funktioniert.
Außerdem wird immer noch der ProcessInput-Fehler für meine ursprüngliche NV12-Quelle mit Intel-Encoder angezeigt. Ich habe keine Probleme mit Nvidia MFT und Software-Encodern.
Ausgabe von Intel Hardware MFT: (Bitte sehen Sie sich die Ausgabe des Nvidia-Encoders an.)
Ausgabe von Nvidia Hardware MFT:
Nutzungsstatistiken für Nvidia-Grafiken:
Intel-Grafiknutzungsstatistiken (ich verstehe nicht, warum die GPU-Engine als Videodecodierung angezeigt wird):
ProcessInput
.Antworten:
Ich habe mir deinen Code angesehen.
Laut Ihrem Beitrag vermute ich ein Intel-Videoprozessor-Problem.
Mein Betriebssystem ist Win7, daher entscheide ich mich, das Verhalten des Videoprozessors mit einem D3D9Device auf meiner Nvidia-Karte und dann auf einem Intel HD Graphics 4000 zu testen.
Ich nehme an, dass sich die Videoprozessorfunktionen für ein D3D9-Gerät genauso verhalten wie für ein D3D11-Gerät. Natürlich muss dies überprüft werden.
Also habe ich dieses Programm gemacht, um Folgendes zu überprüfen: https://github.com/mofo7777/DirectXVideoScreen (siehe D3D9VideoProcessor-Unterprojekt)
Anscheinend überprüfen Sie nicht genügend Informationen über die Funktionen des Videoprozessors.
Mit IDXVAHD_Device :: GetVideoProcessorDeviceCaps überprüfe ich Folgendes:
DXVAHD_VPDEVCAPS.MaxInputStreams> 0
DXVAHD_VPDEVCAPS.VideoProcessorCount> 0
DXVAHD_VPDEVCAPS.OutputFormatCount> 0
DXVAHD_VPDEVCAPS.InputFormatCount> 0
DXVAHD_VPDEVCAPS.InputPool == D3DPOOL_DEFAULT
Ich überprüfe auch das Eingabe- und Ausgabeformat, das von IDXVAHD_Device :: GetVideoProcessorOutputFormats und IDXVAHD_Device :: GetVideoProcessorInputFormats unterstützt wird.
Hier habe ich einen Unterschied zwischen Nvidia GPU und Intel GPU gefunden.
NVIDIA: 4 Ausgabeformat
INTEL: 3 Ausgabeformat
Unter Intel HD Graphics 4000 wird das NV12-Ausgabeformat nicht unterstützt.
Damit das Programm ordnungsgemäß funktioniert, muss der Stream-Status eingerichtet werden, bevor VideoProcessBltHD verwendet werden kann:
Für 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
Könnten Sie zuerst die Videoprozessorfunktionen Ihrer GPU überprüfen? Sehen Sie den gleichen Unterschied wie ich?
Dies ist das erste, was wir wissen müssen, und es scheint, dass Ihr Programm dies nicht überprüft, wie ich es in Ihrem Github-Projekt gesehen habe.
quelle
Wie im Beitrag erwähnt, wurde der Fehler MEError ("Nicht spezifizierter Fehler") vom Ereignisgenerator von Transform unmittelbar nach dem Einspeisen des ersten Eingabebeispiels auf Intel-Hardware zurückgegeben, und weitere Aufrufe gaben nur "Transform Need more input" zurück, aber es wurde keine Ausgabe erzeugt . Der gleiche Code funktionierte jedoch auf Nvidia-Computern einwandfrei. Nachdem ich viel experimentiert und recherchiert hatte, stellte ich fest, dass ich zu viele Instanzen von D3d11Device erstellt habe. In meinem Fall habe ich 2 bis 3 Geräte für die Erfassung, Farbkonvertierung bzw. den Hardware-Encoder erstellt. Ich hätte einfach eine einzelne D3dDevice-Instanz wiederverwenden können. Das Erstellen mehrerer D3d11Device-Instanzen funktioniert jedoch möglicherweise auf High-End-Computern. Dies ist nirgendwo dokumentiert. Ich konnte nicht einmal einen Hinweis auf die Ursachen des "MEError" -Fehlers finden. Es wird nirgendwo erwähnt.
Die Wiederverwendung der D3D11Device-Instanz löste das Problem. Das Posten dieser Lösung kann hilfreich sein für Personen, die mit demselben Problem wie ich konfrontiert sind.
quelle