UPDATE Erfolg !! siehe bearbeiten unten
Einige Teil Erfolg, aber vielleicht genug, um Ihre Frage zu beantworten. Bitte lesen Sie weiter.
Auf meinem System hat das Debuggen der Ausnahme gezeigt, dass die OnTimer()
-Funktion beim Versuch fehlgeschlagen ist, TransferVideoFrame()
aufzurufen. Der Fehler war InvalidArgumentException
.
Also, ein bisschen Googeln führte zu meiner ersten Entdeckung - es gibt anscheinend a bug in NVIDIA drivers - was bedeutet, die Videowiedergabe scheint mit 11 und 10 Feature-Ebenen zu versagen.
So war meine erste Änderung in der Funktion CreateDX11Device()
wie folgt:
static const D3D_FEATURE_LEVEL levels[] = {
/*
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
};
Jetzt TransferVideoFrame()
immer noch nicht, sondern gibt E_FAIL
(als HRESULT) anstelle eines ungültigen Argument.
Mehr führte googeln meiner second discovery -
, die ein Beispiel war die Verwendung von TransferVideoFrame()
zeigt ohne CreateTexture2D()
mit der Textur vorab erstellen. Ich sehe, Sie hatten bereits einen Code in ähnlich zu diesem, der aber nicht verwendet wurde, also haben Sie wahrscheinlich den gleichen Link gefunden.
Wie auch immer, ich jetzt diesen Code verwendet, um die Video-Frame zu erhalten:
ComPtr <ID3D11Texture2D> spTextureDst;
m_spDX11SwapChain->GetBuffer (0, IID_PPV_ARGS (&spTextureDst));
m_spMediaEngine->TransferVideoFrame (spTextureDst.Get(), nullptr, &m_rcTarget, &m_bkgColor);
Nachdem Sie das getan, ich sehe, dass TransferVideoFrame()
erfolgreich ist (gut!) Aber Map()
auf kopierte Textur Aufruf - m_spCopyTexture
- weil das fehlschlägt Textur wurde nicht mit CPU-Lesezugriff erstellt.
So, ich habe nur Ihre Lese-/Schreib-m_spRenderTexture
als Ziel der Kopie verwendet, weil das die richtigen Flags hat und aufgrund der vorherigen Änderung, die ich nicht mehr verwendet wurde.
//copy the render target texture to the readable texture.
m_spDX11DeviceContext->CopySubresourceRegion(m_spRenderTexture.Get(),0,0,0,0,spTextureDst.Get(),0,NULL);
m_spDX11DeviceContext->Flush();
//Map the readable texture;
D3D11_MAPPED_SUBRESOURCE mapped = {0};
HRESULT hr = m_spDX11DeviceContext->Map(m_spRenderTexture.Get(),0,D3D11_MAP_READ,0,&mapped);
void* buffer = ::CoTaskMemAlloc(176 * 144 * 3);
memcpy(buffer, mapped.pData,176 * 144 * 3);
//unmap so we can copy during next update.
m_spDX11DeviceContext->Unmap(m_spRenderTexture.Get(),0);
nun auf meinem System, die OnTimer()
Funktion nicht ausfällt. Videobilder werden an die Textur gerendert und die Pixeldaten werden erfolgreich in den Speicherpuffer kopiert.
Bevor Sie nachsehen, ob es weitere Probleme gibt, ist es vielleicht eine gute Zeit, um zu sehen, ob Sie den gleichen Fortschritt wie bisher machen können. Wenn Sie diese Antwort mit weiteren Informationen kommentieren, bearbeite ich die Antwort, um nach Möglichkeit weitere Hilfe hinzuzufügen.
EDIT
Änderungen an Textur Beschreibung in FramePlayer::CreateBackBuffers()
//make first texture cpu readable
D3D11_TEXTURE2D_DESC texDesc = {0};
texDesc.Width = 176;
texDesc.Height = 144;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
texDesc.MiscFlags = 0;
MEDIA::ThrowIfFailed(m_spDX11Device->CreateTexture2D(&texDesc,NULL,&m_spRenderTexture));
Beachten Sie auch, dass ein Speicherleck gibt es, dass irgendwann geklärt werden muss (ich bin sicher, dass Sie sich bewusst sind) - der Speicher in der folgenden Zeile zugeordnet wird nie freigegeben:
void* buffer = ::CoTaskMemAlloc(176 * 144 * 3); // sizes changed for my test
ERFOLG
Es ist mir jetzt gelungen, ein einzelnes Bild zu speichern, aber jetzt ohne die Verwendung der Kopiestruktur.
Zuerst habe ich die neueste Version von the DirectXTex Library heruntergeladen, die DX11 Textur Hilfsfunktionen bietet, zum Beispiel ein Bild aus einer Textur zu extrahieren und in Datei zu speichern. The instructions zum Hinzufügen der DirectXTex-Bibliothek zu Ihrer Lösung, da ein vorhandenes Projekt sorgfältig befolgt werden muss, wobei die für Windows 8 Store-Apps erforderlichen Änderungen zu beachten sind.
Einmal wird die obige Bibliothek enthalten, verwiesen wird und gebaut, fügen Sie die folgenden #include
‚s FramePlayer.cpp
#include "..\DirectXTex\DirectXTex.h" // nb - use the relative path you copied to
#include <wincodec.h>
Schließlich ist der zentrale Abschnitt des Codes in FramePlayer::OnTimer()
Bedürfnisse der folgenden ähnlich sein. Du wirst sehen, ich speichere nur jedes Mal den gleichen Dateinamen, so dass dies geändert werden muss, um z.B.eine Rahmennummer an den Namen
// new frame available at the media engine so get it
ComPtr<ID3D11Texture2D> spTextureDst;
MEDIA::ThrowIfFailed(m_spDX11SwapChain->GetBuffer(0, IID_PPV_ARGS(&spTextureDst)));
auto rcNormalized = MFVideoNormalizedRect();
rcNormalized.left = 0;
rcNormalized.right = 1;
rcNormalized.top = 0;
rcNormalized.bottom = 1;
MEDIA::ThrowIfFailed(m_spMediaEngine->TransferVideoFrame(spTextureDst.Get(), &rcNormalized, &m_rcTarget, &m_bkgColor));
// capture an image from the DX11 texture
DirectX::ScratchImage pImage;
HRESULT hr = DirectX::CaptureTexture(m_spDX11Device.Get(), m_spDX11DeviceContext.Get(), spTextureDst.Get(), pImage);
if (SUCCEEDED(hr))
{
// get the image object from the wrapper
const DirectX::Image *pRealImage = pImage.GetImage(0, 0, 0);
// set some place to save the image frame
StorageFolder ^dataFolder = ApplicationData::Current->LocalFolder;
Platform::String ^szPath = dataFolder->Path + "\\frame.png";
// save the image to file
hr = DirectX::SaveToWICFile(*pRealImage, DirectX::WIC_FLAGS_NONE, GUID_ContainerFormatPng, szPath->Data());
}
// and the present it to the screen
MEDIA::ThrowIfFailed(m_spDX11SwapChain->Present(1, 0));
Ich habe keine Zeit jetzt diese noch weiter zu nehmen, aber ich bin sehr zufrieden mit dem, was ich bisher erreicht habe :-))
Können Sie ein frisches Aussehen und aktualisieren Sie Ihre Ergebnisse in Kommentaren?
Kannst du mir bitte meinen Code mitteilen, da ich gerade eine App für WP 8.1 entwickle und mir die C++ Programmierung nicht bekannt ist? Es wäre eine große Hilfe von dir. – Xyroid
Hallo, Ich arbeite an einer Winrt App und ich muss auch Frames aus Video extrahieren. Ich habe deinen Code benutzt, aber es extrahiert nur die ersten 2 Frames. Gibt es etwas Besonderes zu ändern? Ich habe den Code aktualisiert, wie Sie in der Bearbeitung dieser Frage erwähnt haben. –
Können Sie Arbeitscode freigeben? –