Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mpc-hc/LAVFilters.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHendrik Leppkes <h.leppkes@gmail.com>2012-02-14 23:38:35 +0400
committerHendrik Leppkes <h.leppkes@gmail.com>2012-02-16 19:59:34 +0400
commitcd28fb752cf4e35ca9cd49bba0e3305766da14d9 (patch)
tree6fd62bd50f08d99f8e26175494ae05f16fe3e65c
parent565be07c7eb82bf5429a04bc7a616c331e89602a (diff)
Implement native DXVA2 decoding
-rw-r--r--decoder/LAVVideo/LAVPixFmtConverter.cpp2
-rw-r--r--decoder/LAVVideo/LAVVideo.cpp118
-rw-r--r--decoder/LAVVideo/LAVVideo.h4
-rw-r--r--decoder/LAVVideo/LAVVideo.vcxproj4
-rw-r--r--decoder/LAVVideo/LAVVideo.vcxproj.filters18
-rw-r--r--decoder/LAVVideo/LAVVideoSettings.h4
-rw-r--r--decoder/LAVVideo/VideoOutputPin.cpp45
-rw-r--r--decoder/LAVVideo/VideoOutputPin.h33
-rw-r--r--decoder/LAVVideo/VideoSettingsProp.cpp6
-rw-r--r--decoder/LAVVideo/decoders/DecBase.h2
-rw-r--r--decoder/LAVVideo/decoders/ILAVDecoder.h17
-rw-r--r--decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp239
-rw-r--r--decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h90
-rw-r--r--decoder/LAVVideo/decoders/dxva2dec.cpp387
-rw-r--r--decoder/LAVVideo/decoders/dxva2dec.h20
15 files changed, 874 insertions, 115 deletions
diff --git a/decoder/LAVVideo/LAVPixFmtConverter.cpp b/decoder/LAVVideo/LAVPixFmtConverter.cpp
index abd9e625..fe257dff 100644
--- a/decoder/LAVVideo/LAVPixFmtConverter.cpp
+++ b/decoder/LAVVideo/LAVPixFmtConverter.cpp
@@ -85,6 +85,8 @@ static LAV_INOUT_PIXFMT_MAP lav_pixfmt_map[] = {
{ LAVPixFmt_RGB24, 8, 6, { LAVOutPixFmt_RGB24, LAVOutPixFmt_RGB32, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } },
{ LAVPixFmt_RGB32, 8, 6, { LAVOutPixFmt_RGB32, LAVOutPixFmt_RGB24, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } },
{ LAVPixFmt_ARGB32, 8, 6, { LAVOutPixFmt_RGB32, LAVOutPixFmt_RGB24, LAVOutPixFmt_YUY2, LAVOutPixFmt_UYVY, LAVOutPixFmt_NV12, LAVOutPixFmt_YV12 } },
+
+ { LAVPixFmt_DXVA2, 7, 1, { LAVOutPixFmt_NV12 } },
};
LAVOutPixFmtDesc lav_pixfmt_desc[] = {
diff --git a/decoder/LAVVideo/LAVVideo.cpp b/decoder/LAVVideo/LAVVideo.cpp
index a6a24f3b..a4e67620 100644
--- a/decoder/LAVVideo/LAVVideo.cpp
+++ b/decoder/LAVVideo/LAVVideo.cpp
@@ -23,6 +23,8 @@
#include "Media.h"
#include <dvdmedia.h>
+#include "VideoOutputPin.h"
+
#include "moreuuids.h"
#include "registry.h"
@@ -77,6 +79,23 @@ CLAVVideo::CLAVVideo(LPUNKNOWN pUnk, HRESULT* phr)
GetModuleFileName(NULL, fileName, 1024);
m_processName = PathFindFileName (fileName);
+ m_pInput = new CTransformInputPin(TEXT("CTransformInputPin"), this, phr, L"Input");
+ if(!m_pInput) {
+ *phr = E_OUTOFMEMORY;
+ }
+ if (FAILED(*phr)) {
+ return;
+ }
+
+ m_pOutput = new CVideoOutputPin(TEXT("CVideoOutputPin"), this, phr, L"Output");
+ if(!m_pOutput) {
+ *phr = E_OUTOFMEMORY;
+ }
+ if(FAILED(*phr)) {
+ SAFE_DELETE(m_pInput);
+ return;
+ }
+
memset(&m_LAVPinInfo, 0, sizeof(m_LAVPinInfo));
avcodec_register_all();
@@ -513,8 +532,10 @@ HRESULT CLAVVideo::CreateDecoder(const CMediaType *pmt)
m_pDecoder = CreateDecoderCUVID();
else if (m_settings.HWAccel == HWAccel_QuickSync)
m_pDecoder = CreateDecoderQuickSync();
- else if (m_settings.HWAccel == HWAccel_DXVA2)
+ else if (m_settings.HWAccel == HWAccel_DXVA2CopyBack)
m_pDecoder = CreateDecoderDXVA2();
+ else if (m_settings.HWAccel == HWAccel_DXVA2Native)
+ m_pDecoder = CreateDecoderDXVA2Native();
m_bHWDecoder = TRUE;
}
@@ -619,6 +640,23 @@ HRESULT CLAVVideo::BreakConnect(PIN_DIRECTION dir)
return __super::BreakConnect(dir);
}
+HRESULT CLAVVideo::CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin)
+{
+ DbgLog((LOG_TRACE, 10, L"::CompleteConnect"));
+ HRESULT hr = S_OK;
+ if (dir == PINDIR_OUTPUT) {
+ if (m_pDecoder) {
+ hr = m_pDecoder->PostConnect(pReceivePin);
+ if (FAILED(hr)) {
+ m_bHWDecoderFailed = TRUE;
+ CMediaType &mt = m_pInput->CurrentMediaType();
+ hr = CreateDecoder(&mt);
+ }
+ }
+ }
+ return S_OK;
+}
+
HRESULT CLAVVideo::GetDeliveryBuffer(IMediaSample** ppOut, int width, int height, AVRational ar, DXVA2_ExtendedFormat dxvaExtFlags, REFERENCE_TIME avgFrameDuration)
{
CheckPointer(ppOut, E_POINTER);
@@ -1005,7 +1043,10 @@ STDMETHODIMP CLAVVideo::Deliver(LAVFrame *pFrame)
return S_OK;
}
- return Filter(pFrame);
+ if (pFrame->format == LAVPixFmt_DXVA2)
+ return DeliverToRenderer(pFrame);
+ else
+ return Filter(pFrame);
}
STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
@@ -1046,9 +1087,14 @@ STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
if (avgDuration == 0)
avgDuration = AV_NOPTS_VALUE;
- if(FAILED(hr = GetDeliveryBuffer(&pSampleOut, width, height, pFrame->aspect_ratio, pFrame->ext_format, avgDuration)) || FAILED(hr = pSampleOut->GetPointer(&pDataOut))) {
- ReleaseFrame(&pFrame);
- return hr;
+ if (pFrame->format == LAVPixFmt_DXVA2) {
+ pSampleOut = (IMediaSample *)pFrame->data[0];
+ pSampleOut->AddRef();
+ } else {
+ if(FAILED(hr = GetDeliveryBuffer(&pSampleOut, width, height, pFrame->aspect_ratio, pFrame->ext_format, avgDuration)) || FAILED(hr = pSampleOut->GetPointer(&pDataOut))) {
+ ReleaseFrame(&pFrame);
+ return hr;
+ }
}
pSampleOut->SetTime(&pFrame->rtStart, &pFrame->rtStop);
@@ -1058,34 +1104,35 @@ STDMETHODIMP CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
BITMAPINFOHEADER *pBIH = NULL;
videoFormatTypeHandler(mt.Format(), mt.FormatType(), &pBIH);
- long required = pBIH->biSizeImage;
-
- long lSampleSize = pSampleOut->GetSize();
- if (lSampleSize < required) {
- DbgLog((LOG_ERROR, 10, L"::Decode(): Buffer is too small! Actual: %d, Required: %d", lSampleSize, required));
- SafeRelease(&pSampleOut);
- ReleaseFrame(&pFrame);
- return E_FAIL;
- }
-
-
-#if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS
- LARGE_INTEGER frequency, start, end;
- QueryPerformanceFrequency(&frequency);
- QueryPerformanceCounter(&start);
-#endif
- m_PixFmtConverter.Convert(pFrame, pDataOut, width, height, pBIH->biWidth);
-#if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS
- QueryPerformanceCounter(&end);
- double diff = (end.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart;
- m_pixFmtTimingAvg.Sample(diff);
+ if (pFrame->format != LAVPixFmt_DXVA2) {
+ long required = pBIH->biSizeImage;
- DbgLog((LOG_TRACE, 10, L"Pixel Mapping took %2.3fms in avg", m_pixFmtTimingAvg.Average()));
-#endif
+ long lSampleSize = pSampleOut->GetSize();
+ if (lSampleSize < required) {
+ DbgLog((LOG_ERROR, 10, L"::Decode(): Buffer is too small! Actual: %d, Required: %d", lSampleSize, required));
+ SafeRelease(&pSampleOut);
+ ReleaseFrame(&pFrame);
+ return E_FAIL;
+ }
- if ((mt.subtype == MEDIASUBTYPE_RGB32 || mt.subtype == MEDIASUBTYPE_RGB24) && pBIH->biHeight > 0) {
- int bpp = (mt.subtype == MEDIASUBTYPE_RGB32) ? 4 : 3;
- flip_plane(pDataOut, pBIH->biWidth * bpp, height);
+ #if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS
+ LARGE_INTEGER frequency, start, end;
+ QueryPerformanceFrequency(&frequency);
+ QueryPerformanceCounter(&start);
+ #endif
+ m_PixFmtConverter.Convert(pFrame, pDataOut, width, height, pBIH->biWidth);
+ #if defined(DEBUG) && DEBUG_PIXELCONV_TIMINGS
+ QueryPerformanceCounter(&end);
+ double diff = (end.QuadPart - start.QuadPart) * 1000.0 / frequency.QuadPart;
+ m_pixFmtTimingAvg.Sample(diff);
+
+ DbgLog((LOG_TRACE, 10, L"Pixel Mapping took %2.3fms in avg", m_pixFmtTimingAvg.Average()));
+ #endif
+
+ if ((mt.subtype == MEDIASUBTYPE_RGB32 || mt.subtype == MEDIASUBTYPE_RGB24) && pBIH->biHeight > 0) {
+ int bpp = (mt.subtype == MEDIASUBTYPE_RGB32) ? 4 : 3;
+ flip_plane(pDataOut, pBIH->biWidth * bpp, height);
+ }
}
if (m_bSendMediaType) {
@@ -1273,13 +1320,20 @@ STDMETHODIMP_(DWORD) CLAVVideo::CheckHWAccelSupport(LAVHWAccel hwAccel)
SAFE_DELETE(pDecoder);
}
break;
- case HWAccel_DXVA2:
+ case HWAccel_DXVA2CopyBack:
{
ILAVDecoder *pDecoder = CreateDecoderDXVA2();
hr = pDecoder->InitInterfaces(this, this);
SAFE_DELETE(pDecoder);
}
break;
+ case HWAccel_DXVA2Native:
+ {
+ ILAVDecoder *pDecoder = CreateDecoderDXVA2Native();
+ hr = pDecoder->InitInterfaces(this, this);
+ SAFE_DELETE(pDecoder);
+ }
+ break;
}
return SUCCEEDED(hr) ? 1 : 0;
diff --git a/decoder/LAVVideo/LAVVideo.h b/decoder/LAVVideo/LAVVideo.h
index c16110ef..71ec2b62 100644
--- a/decoder/LAVVideo/LAVVideo.h
+++ b/decoder/LAVVideo/LAVVideo.h
@@ -112,6 +112,7 @@ public:
HRESULT Receive(IMediaSample *pIn);
HRESULT BreakConnect(PIN_DIRECTION dir);
+ HRESULT CompleteConnect(PIN_DIRECTION dir, IPin *pReceivePin);
// ILAVVideoCallback
STDMETHODIMP AllocateFrame(LAVFrame **ppFrame);
@@ -125,6 +126,7 @@ public:
STDMETHODIMP_(BOOL) IsVistaOrNewer();
STDMETHODIMP_(CMediaType&) GetInputMediaType() { return m_pInput->CurrentMediaType(); }
STDMETHODIMP GetLAVPinInfo(LAVPinInfo &info) { if (m_LAVPinInfoValid) { info = m_LAVPinInfo; return S_OK; } return E_FAIL; }
+ STDMETHODIMP_(CBasePin*) GetOutputPin() { return m_pOutput; }
public:
// Pin Configuration
@@ -152,6 +154,8 @@ private:
STDMETHODIMP DeliverToRenderer(LAVFrame *pFrame);
private:
+ friend class CVideoOutputPin;
+
ILAVDecoder *m_pDecoder;
REFERENCE_TIME m_rtPrevStart;
diff --git a/decoder/LAVVideo/LAVVideo.vcxproj b/decoder/LAVVideo/LAVVideo.vcxproj
index e40aeb25..8e7bd56a 100644
--- a/decoder/LAVVideo/LAVVideo.vcxproj
+++ b/decoder/LAVVideo/LAVVideo.vcxproj
@@ -147,6 +147,7 @@
<ClCompile Include="decoders\avcodec.cpp" />
<ClCompile Include="decoders\cuvid.cpp" />
<ClCompile Include="decoders\dxva2dec.cpp" />
+ <ClCompile Include="decoders\dxva2\DXVA2SurfaceAllocator.cpp" />
<ClCompile Include="decoders\pixfmt.cpp" />
<ClCompile Include="decoders\quicksync.cpp" />
<ClCompile Include="dllmain.cpp" />
@@ -169,6 +170,7 @@
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="VideoOutputPin.cpp" />
<ClCompile Include="VideoSettingsProp.cpp" />
</ItemGroup>
<ItemGroup>
@@ -176,6 +178,7 @@
<ClInclude Include="decoders\cuvid.h" />
<ClInclude Include="decoders\DecBase.h" />
<ClInclude Include="decoders\dxva2dec.h" />
+ <ClInclude Include="decoders\dxva2\DXVA2SurfaceAllocator.h" />
<ClInclude Include="decoders\ILAVDecoder.h" />
<ClInclude Include="decoders\quicksync.h" />
<ClInclude Include="H264RandomAccess.h" />
@@ -191,6 +194,7 @@
<ClInclude Include="pixconv\pixconv_sse2_templates.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
+ <ClInclude Include="VideoOutputPin.h" />
<ClInclude Include="VideoSettingsProp.h" />
</ItemGroup>
<ItemGroup>
diff --git a/decoder/LAVVideo/LAVVideo.vcxproj.filters b/decoder/LAVVideo/LAVVideo.vcxproj.filters
index 02737b4d..ef11eb0d 100644
--- a/decoder/LAVVideo/LAVVideo.vcxproj.filters
+++ b/decoder/LAVVideo/LAVVideo.vcxproj.filters
@@ -31,6 +31,12 @@
<Filter Include="Header Files\decoders">
<UniqueIdentifier>{c5cd3602-30d6-48ff-8100-230758b36fb4}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\decoders\dxva2">
+ <UniqueIdentifier>{2417dd22-d693-40ad-a3f7-a6e3b4e22b9d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\decoders\dxva2">
+ <UniqueIdentifier>{d3bbf10b-cfb1-45d9-8cb4-6323349e6b22}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@@ -105,6 +111,12 @@
<ClCompile Include="decoders\dxva2dec.cpp">
<Filter>Source Files\decoders</Filter>
</ClCompile>
+ <ClCompile Include="VideoOutputPin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="decoders\dxva2\DXVA2SurfaceAllocator.cpp">
+ <Filter>Source Files\decoders\dxva2</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@@ -167,6 +179,12 @@
<ClInclude Include="decoders\dxva2dec.h">
<Filter>Header Files\decoders</Filter>
</ClInclude>
+ <ClInclude Include="VideoOutputPin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="decoders\dxva2\DXVA2SurfaceAllocator.h">
+ <Filter>Header Files\decoders\dxva2</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="LAVVideo.rc">
diff --git a/decoder/LAVVideo/LAVVideoSettings.h b/decoder/LAVVideo/LAVVideoSettings.h
index 3715cb4d..a73ff613 100644
--- a/decoder/LAVVideo/LAVVideoSettings.h
+++ b/decoder/LAVVideo/LAVVideoSettings.h
@@ -91,7 +91,9 @@ typedef enum LAVHWAccel {
HWAccel_None,
HWAccel_CUDA,
HWAccel_QuickSync,
- HWAccel_DXVA2
+ HWAccel_DXVA2,
+ HWAccel_DXVA2CopyBack = HWAccel_DXVA2,
+ HWAccel_DXVA2Native
};
// Deinterlace algorithms offered by the hardware decoders
diff --git a/decoder/LAVVideo/VideoOutputPin.cpp b/decoder/LAVVideo/VideoOutputPin.cpp
new file mode 100644
index 00000000..42f59225
--- /dev/null
+++ b/decoder/LAVVideo/VideoOutputPin.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010-2012 Hendrik Leppkes
+ * http://www.1f0.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "stdafx.h"
+#include "LAVVideo.h"
+#include "VideoOutputPin.h"
+
+CVideoOutputPin::CVideoOutputPin(LPCTSTR pObjectName, CLAVVideo *pFilter, HRESULT * phr, LPCWSTR pName)
+ : CTransformOutputPin(pObjectName, (CTransformFilter *)pFilter, phr, pName)
+ , m_pFilter(pFilter)
+{
+}
+
+CVideoOutputPin::~CVideoOutputPin()
+{
+}
+
+HRESULT CVideoOutputPin::InitAllocator(IMemAllocator **ppAlloc)
+{
+ HRESULT hr = S_FALSE;
+ if (m_pFilter->m_pDecoder) {
+ hr = m_pFilter->m_pDecoder->InitAllocator(ppAlloc);
+ }
+
+ if (hr != S_OK)
+ hr = __super::InitAllocator(ppAlloc);
+
+ return hr;
+}
diff --git a/decoder/LAVVideo/VideoOutputPin.h b/decoder/LAVVideo/VideoOutputPin.h
new file mode 100644
index 00000000..52867773
--- /dev/null
+++ b/decoder/LAVVideo/VideoOutputPin.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010-2012 Hendrik Leppkes
+ * http://www.1f0.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+class CVideoOutputPin : public CTransformOutputPin
+{
+public:
+ CVideoOutputPin(LPCTSTR pObjectName, CLAVVideo *pFilter, HRESULT * phr, LPCWSTR pName);
+ virtual ~CVideoOutputPin();
+
+ HRESULT InitAllocator(IMemAllocator **ppAlloc);
+
+private:
+ CLAVVideo *m_pFilter;
+};
+
diff --git a/decoder/LAVVideo/VideoSettingsProp.cpp b/decoder/LAVVideo/VideoSettingsProp.cpp
index df8fb040..bf05bf08 100644
--- a/decoder/LAVVideo/VideoSettingsProp.cpp
+++ b/decoder/LAVVideo/VideoSettingsProp.cpp
@@ -179,11 +179,13 @@ HRESULT CLAVVideoSettingsProp::OnActivate()
WCHAR hwAccelNone[] = L"None";
WCHAR hwAccelCUDA[] = L"NVIDIA CUVID";
WCHAR hwAccelQuickSync[] = L"Intel\xae QuickSync";
- WCHAR hwAccelDXVA2[] = L"DXVA2 (copy-back)";
+ WCHAR hwAccelDXVA2CB[] = L"DXVA2 (copy-back)";
+ WCHAR hwAccelDXVA2N[] = L"DXVA2 (native)";
SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelNone);
SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelCUDA);
SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelQuickSync);
- SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2);
+ SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2CB);
+ SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2N);
// Init the fieldorder Combo Box
SendDlgItemMessage(m_Dlg, IDC_DEINT_FIELDORDER, CB_RESETCONTENT, 0, 0);
diff --git a/decoder/LAVVideo/decoders/DecBase.h b/decoder/LAVVideo/decoders/DecBase.h
index 0d4c989b..6b062322 100644
--- a/decoder/LAVVideo/decoders/DecBase.h
+++ b/decoder/LAVVideo/decoders/DecBase.h
@@ -34,6 +34,8 @@ public:
STDMETHODIMP InitInterfaces(ILAVVideoSettings *pSettings, ILAVVideoCallback *pCallback) { m_pSettings = pSettings; m_pCallback = pCallback; return Init(); }
STDMETHOD_(REFERENCE_TIME, GetFrameDuration)() { return 0; }
STDMETHOD_(BOOL, IsInterlaced)() { return TRUE; }
+ STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc) { return E_NOTIMPL; }
+ STDMETHODIMP PostConnect(IPin *pPin) { return S_FALSE; }
STDMETHODIMP Decode(IMediaSample *pSample) {
HRESULT hr;
diff --git a/decoder/LAVVideo/decoders/ILAVDecoder.h b/decoder/LAVVideo/decoders/ILAVDecoder.h
index 49b29c01..2f5dde21 100644
--- a/decoder/LAVVideo/decoders/ILAVDecoder.h
+++ b/decoder/LAVVideo/decoders/ILAVDecoder.h
@@ -219,6 +219,11 @@ interface ILAVVideoCallback
* Query the LAVPinInfo
*/
STDMETHOD(GetLAVPinInfo)(LAVPinInfo &info) PURE;
+
+ /**
+ * Get a reference to the output pin
+ */
+ STDMETHOD_(CBasePin*, GetOutputPin)() PURE;
};
/**
@@ -299,6 +304,17 @@ interface ILAVDecoder
* This function should return false if the format can 100% not be interlaced, and true if it can be interlaced (but also progressive).
*/
STDMETHOD_(BOOL, IsInterlaced)() PURE;
+
+ /**
+ * Allows the decoder to handle an allocator.
+ * Used by DXVA2 decoding
+ */
+ STDMETHOD(InitAllocator)(IMemAllocator **ppAlloc) PURE;
+
+ /**
+ * Function called after connection is established, with the pin as argument
+ */
+ STDMETHOD(PostConnect)(IPin *pPin) PURE;
};
/**
@@ -310,3 +326,4 @@ ILAVDecoder *CreateDecoderAVCodec();
ILAVDecoder *CreateDecoderCUVID();
ILAVDecoder *CreateDecoderQuickSync();
ILAVDecoder *CreateDecoderDXVA2();
+ILAVDecoder *CreateDecoderDXVA2Native();
diff --git a/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp
new file mode 100644
index 00000000..aae155f2
--- /dev/null
+++ b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.cpp
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2010-2012 Hendrik Leppkes
+ * http://www.1f0.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Initial Design and Concept taken from MPC-HC, licensed under GPLv2
+ */
+
+#include "stdafx.h"
+#include "decoders/dxva2dec.h"
+#include "DXVA2SurfaceAllocator.h"
+
+#include "moreuuids.h"
+#include <evr.h>
+#include <Mferror.h>
+
+CDXVA2Sample::CDXVA2Sample(CDXVA2SurfaceAllocator *pAlloc, HRESULT *phr)
+ : CMediaSample(NAME("CDXVA2Sample"), (CBaseAllocator*)pAlloc, phr, NULL, 0)
+ , m_dwSurfaceId(0), m_pSurface(NULL)
+{
+}
+
+CDXVA2Sample::~CDXVA2Sample()
+{
+ SafeRelease(&m_pSurface);
+}
+
+//Note: CMediaSample does not derive from CUnknown, so we cannot use the
+// DECLARE_IUNKNOWN macro that is used by most of the filter classes.
+
+STDMETHODIMP CDXVA2Sample::QueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ CheckPointer(ppv,E_POINTER);
+ ValidateReadWritePtr(ppv,sizeof(PVOID));
+
+ if (riid == __uuidof(IMFGetService)) {
+ return GetInterface((IMFGetService*) this, ppv);
+ }
+ if (riid == __uuidof(ILAVDXVA2Sample)) {
+ return GetInterface((ILAVDXVA2Sample*) this, ppv);
+ } else {
+ return CMediaSample::QueryInterface(riid, ppv);
+ }
+}
+
+STDMETHODIMP_(ULONG) CDXVA2Sample::AddRef()
+{
+ return __super::AddRef();
+}
+
+STDMETHODIMP_(ULONG) CDXVA2Sample::Release()
+{
+ // Return a temporary variable for thread safety.
+ ULONG cRef = __super::Release();
+ return cRef;
+}
+
+// IMFGetService::GetService
+STDMETHODIMP CDXVA2Sample::GetService(REFGUID guidService, REFIID riid, LPVOID *ppv)
+{
+ if (guidService != MR_BUFFER_SERVICE) {
+ return MF_E_UNSUPPORTED_SERVICE;
+ } else if (m_pSurface == NULL) {
+ return E_NOINTERFACE;
+ } else {
+ return m_pSurface->QueryInterface(riid, ppv);
+ }
+}
+
+// Override GetPointer because this class does not manage a system memory buffer.
+// The EVR uses the MR_BUFFER_SERVICE service to get the Direct3D surface.
+STDMETHODIMP CDXVA2Sample::GetPointer(BYTE ** ppBuffer)
+{
+ return E_NOTIMPL;
+}
+
+// Sets the pointer to the Direct3D surface.
+void CDXVA2Sample::SetSurface(DWORD surfaceId, IDirect3DSurface9 *pSurf)
+{
+ SafeRelease(&m_pSurface);
+ m_pSurface = pSurf;
+ m_dwSurfaceId = surfaceId;
+ if (m_pSurface)
+ m_pSurface->AddRef();
+}
+
+STDMETHODIMP_(int) CDXVA2Sample::GetDXSurfaceId()
+{
+ return m_dwSurfaceId;
+}
+
+CDXVA2SurfaceAllocator::CDXVA2SurfaceAllocator(CDecDXVA2 *m_pDXVA2Dec, HRESULT* phr)
+ : CBaseAllocator(NAME("CDXVA2SurfaceAllocator"), NULL, phr)
+ , m_pDec(m_pDXVA2Dec)
+ , m_ppRTSurfaceArray(NULL)
+ , m_nSurfaceArrayCount(0)
+{
+}
+
+CDXVA2SurfaceAllocator::~CDXVA2SurfaceAllocator(void)
+{
+ if (m_pDec)
+ m_pDec->m_pDXVA2Allocator = NULL;
+}
+
+// IUnknown
+STDMETHODIMP CDXVA2SurfaceAllocator::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+ CheckPointer(ppv, E_POINTER);
+
+ *ppv = NULL;
+
+ return
+ QI(ILAVDXVA2SurfaceAllocator)
+ __super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+HRESULT CDXVA2SurfaceAllocator::Alloc()
+{
+ DbgLog((LOG_TRACE, 10, L"CDXVA2SurfaceAllocator::Alloc()"));
+ HRESULT hr = S_OK;
+ IDirectXVideoDecoderService *pDXVA2Service = NULL;
+
+ if (!m_pDec)
+ return E_FAIL;
+
+ CheckPointer(m_pDec->m_pD3DDevMngr, E_UNEXPECTED);
+ hr = m_pDec->m_pD3DDevMngr->GetVideoService (m_pDec->m_hDevice, IID_IDirectXVideoDecoderService, (void**)&pDXVA2Service);
+ CheckPointer (pDXVA2Service, E_UNEXPECTED);
+ CAutoLock lock(this);
+
+ hr = __super::Alloc();
+
+ if (SUCCEEDED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> Releasing old resources"));
+ // Free the old resources.
+ Free();
+
+ m_nSurfaceArrayCount = m_lCount;
+
+ // Allocate a new array of pointers.
+ m_ppRTSurfaceArray = new IDirect3DSurface9*[m_lCount];
+ if (m_ppRTSurfaceArray == NULL) {
+ hr = E_OUTOFMEMORY;
+ } else {
+ ZeroMemory(m_ppRTSurfaceArray, sizeof(IDirect3DSurface9*) * m_lCount);
+ }
+ }
+
+ // Allocate the surfaces.
+ D3DFORMAT d3dFormat = (D3DFORMAT)FOURCC_NV12;
+ if (SUCCEEDED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> Allocating surfaces"));
+ hr = pDXVA2Service->CreateSurface(
+ m_pDec->m_dwSurfaceWidth,
+ m_pDec->m_dwSurfaceHeight,
+ m_lCount - 1,
+ d3dFormat,
+ D3DPOOL_DEFAULT,
+ 0,
+ DXVA2_VideoDecoderRenderTarget,
+ m_ppRTSurfaceArray,
+ NULL
+ );
+ }
+
+ if (SUCCEEDED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> Creating samples"));
+ // Important : create samples in reverse order !
+ for (m_lAllocated = m_lCount-1; m_lAllocated >= 0; m_lAllocated--) {
+ CDXVA2Sample *pSample = new CDXVA2Sample(this, &hr);
+ if (pSample == NULL) {
+ hr = E_OUTOFMEMORY;
+ break;
+ }
+ if (FAILED(hr)) {
+ break;
+ }
+ // Assign the Direct3D surface pointer and the index.
+ pSample->SetSurface(m_lAllocated, m_ppRTSurfaceArray[m_lAllocated]);
+
+ // Add to the sample list.
+ m_lFree.Add(pSample);
+ }
+
+ hr = m_pDec->CreateDXVA2Decoder(m_lCount, m_ppRTSurfaceArray);
+ if (FAILED (hr)) {
+ Free();
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ m_bChanged = FALSE;
+ }
+ SafeRelease(&pDXVA2Service);
+ return hr;
+}
+
+void CDXVA2SurfaceAllocator::Free()
+{
+ DbgLog((LOG_TRACE, 10, L"CDXVA2SurfaceAllocator::Free()"));
+ CMediaSample *pSample = NULL;
+
+ if (m_pDec)
+ m_pDec->Flush();
+
+ do {
+ pSample = m_lFree.RemoveHead();
+ if (pSample) {
+ delete pSample;
+ }
+ } while (pSample);
+
+ if (m_ppRTSurfaceArray) {
+ for (UINT i = 0; i < m_nSurfaceArrayCount; i++) {
+ if (m_ppRTSurfaceArray[i] != NULL) {
+ m_ppRTSurfaceArray[i]->Release();
+ }
+ }
+
+ delete [] m_ppRTSurfaceArray;
+ m_ppRTSurfaceArray = NULL;
+ }
+ m_lAllocated = 0;
+ m_nSurfaceArrayCount = 0;
+}
diff --git a/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h
new file mode 100644
index 00000000..022c6caf
--- /dev/null
+++ b/decoder/LAVVideo/decoders/dxva2/DXVA2SurfaceAllocator.h
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2010-2012 Hendrik Leppkes
+* http://www.1f0.de
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*
+* Initial Design and Concept taken from MPC-HC, licensed under GPLv2
+*/
+
+#pragma once
+
+#include <Mfidl.h>
+
+class CDecDXVA2;
+
+interface __declspec(uuid("50A8A9A1-FF44-45C1-9DC2-79066ED1E576"))
+ILAVDXVA2Sample :
+public IUnknown {
+ STDMETHOD_(int, GetDXSurfaceId()) = 0;
+};
+
+class CDXVA2Sample : public CMediaSample, public IMFGetService, public ILAVDXVA2Sample
+{
+ friend class CDXVA2SurfaceAllocator;
+
+public:
+ CDXVA2Sample(CDXVA2SurfaceAllocator *pAlloc, HRESULT *phr);
+ virtual ~CDXVA2Sample();
+
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IMFGetService::GetService
+ STDMETHODIMP GetService(REFGUID guidService, REFIID riid, LPVOID *ppv);
+
+ // ILAVDXVA2Sample
+ STDMETHODIMP_(int) GetDXSurfaceId();
+
+ // Override GetPointer because this class does not manage a system memory buffer.
+ // The EVR uses the MR_BUFFER_SERVICE service to get the Direct3D surface.
+ STDMETHODIMP GetPointer(BYTE ** ppBuffer);
+
+private:
+
+ // Sets the pointer to the Direct3D surface.
+ void SetSurface(DWORD surfaceId, IDirect3DSurface9 *pSurf);
+
+ IDirect3DSurface9 *m_pSurface;
+ DWORD m_dwSurfaceId;
+};
+
+interface __declspec(uuid("23F80BD8-2654-4F74-B7CC-621868D0A850"))
+ILAVDXVA2SurfaceAllocator :
+public IUnknown {
+ STDMETHOD_(void,DecoderDestruct)() = 0;
+};
+
+class CDXVA2SurfaceAllocator : public CBaseAllocator, public ILAVDXVA2SurfaceAllocator
+{
+public:
+ CDXVA2SurfaceAllocator(CDecDXVA2 *m_pDXVA2Dec, HRESULT* phr);
+ virtual ~CDXVA2SurfaceAllocator(void);
+
+ DECLARE_IUNKNOWN;
+ STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+ HRESULT Alloc();
+ void Free();
+
+ STDMETHODIMP_(void) DecoderDestruct() { m_pDec = NULL; }
+
+private:
+ CDecDXVA2 *m_pDec;
+
+ IDirect3DSurface9 **m_ppRTSurfaceArray;
+ UINT m_nSurfaceArrayCount;
+};
diff --git a/decoder/LAVVideo/decoders/dxva2dec.cpp b/decoder/LAVVideo/decoders/dxva2dec.cpp
index c2adecfc..bc6b476a 100644
--- a/decoder/LAVVideo/decoders/dxva2dec.cpp
+++ b/decoder/LAVVideo/decoders/dxva2dec.cpp
@@ -21,12 +21,14 @@
#include "stdafx.h"
#include "dxva2dec.h"
+#include "dxva2/DXVA2SurfaceAllocator.h"
#include "moreuuids.h"
#include "Media.h"
#include <Shlwapi.h>
#include <dxva2api.h>
+#include <evr.h>
#include "libavcodec/dxva2.h"
#include "gpu_memcpy_sse4.h"
@@ -39,6 +41,12 @@ ILAVDecoder *CreateDecoderDXVA2() {
return new CDecDXVA2();
}
+ILAVDecoder *CreateDecoderDXVA2Native() {
+ CDecDXVA2 *dec = new CDecDXVA2();
+ dec->SetNativeMode(TRUE);
+ return dec;
+}
+
////////////////////////////////////////////////////////////////////////////////
// Codec Maps
////////////////////////////////////////////////////////////////////////////////
@@ -144,6 +152,7 @@ static void CopyFrameNV12_SSE4(const BYTE *pSourceData, BYTE *pY, BYTE *pUV, int
CDecDXVA2::CDecDXVA2(void)
: CDecAvcodec()
+ , m_bNative(FALSE)
, m_pD3D(NULL)
, m_pD3DDev(NULL)
, m_pD3DDevMngr(NULL)
@@ -157,6 +166,8 @@ CDecDXVA2::CDecDXVA2(void)
, m_dwSurfaceWidth(0)
, m_dwSurfaceHeight(0)
, m_dwVendorId(0)
+ , m_pDXVA2Allocator(NULL)
+ , m_hDevice(INVALID_HANDLE_VALUE)
{
ZeroMemory(&dx, sizeof(dx));
ZeroMemory(&m_DXVAExtendedFormat, sizeof(m_DXVAExtendedFormat));
@@ -167,6 +178,8 @@ CDecDXVA2::CDecDXVA2(void)
CDecDXVA2::~CDecDXVA2(void)
{
DestroyDecoder(true);
+ if (m_pDXVA2Allocator)
+ m_pDXVA2Allocator->DecoderDestruct();
}
STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec)
@@ -192,6 +205,9 @@ STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec)
if (bFull) {
SafeRelease(&m_pDXVADecoderService);
+ if (m_hDevice != INVALID_HANDLE_VALUE)
+ m_pD3DDevMngr->CloseDeviceHandle(m_hDevice);
+ m_hDevice = INVALID_HANDLE_VALUE;
SafeRelease(&m_pD3DDevMngr);
SafeRelease(&m_pD3DDev);
SafeRelease(&m_pD3D);
@@ -203,6 +219,103 @@ STDMETHODIMP CDecDXVA2::DestroyDecoder(bool bFull, bool bNoAVCodec)
return S_OK;
}
+STDMETHODIMP CDecDXVA2::InitAllocator(IMemAllocator **ppAlloc)
+{
+ HRESULT hr = S_OK;
+ if (!m_bNative)
+ return E_NOTIMPL;
+
+ m_pDXVA2Allocator = new CDXVA2SurfaceAllocator(this, &hr);
+ if (!m_pDXVA2Allocator) {
+ return E_OUTOFMEMORY;
+ }
+ if (FAILED(hr)) {
+ SAFE_DELETE(m_pDXVA2Allocator);
+ return hr;
+ }
+
+ return m_pDXVA2Allocator->QueryInterface(__uuidof(IMemAllocator), (void **)ppAlloc);
+}
+
+STDMETHODIMP CDecDXVA2::PostConnect(IPin *pPin)
+{
+ HRESULT hr = S_OK;
+ if (m_pD3DDevMngr)
+ return S_FALSE;
+
+ DbgLog((LOG_TRACE, 10, L"CDecDXVA2::PostConnect()"));
+
+ IMFGetService *pGetService = NULL;
+ IDirect3DDeviceManager9 *pDeviceManager = NULL;
+
+ hr = pPin->QueryInterface(__uuidof(IMFGetService), (void**)&pGetService);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> IMFGetService not available"));
+ goto done;
+ }
+
+ // Get the Direct3D device manager.
+ hr = pGetService->GetService(MR_VIDEO_ACCELERATION_SERVICE, __uuidof(IDirect3DDeviceManager9), (void**)&pDeviceManager);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> D3D Device Manager not available"));
+ goto done;
+ }
+
+ hr = SetD3DDeviceManager(pDeviceManager);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> Setting D3D Device Manager faield"));
+ goto done;
+ }
+
+ hr = DXVA2NotifyEVR();
+
+done:
+ SafeRelease(&pGetService);
+ if (FAILED(hr)) {
+ SafeRelease(&pDeviceManager);
+ }
+ return hr;
+}
+
+HRESULT CDecDXVA2::DXVA2NotifyEVR()
+{
+ HRESULT hr = S_OK;
+ IMFGetService *pGetService = NULL;
+ IDirectXVideoMemoryConfiguration *pVideoConfig = NULL;
+
+ hr = m_pCallback->GetOutputPin()->GetConnected()->QueryInterface(&pGetService);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> IMFGetService not available"));
+ goto done;
+ }
+
+ // Configure EVR for receiving DXVA2 samples
+ hr = pGetService->GetService(MR_VIDEO_ACCELERATION_SERVICE, __uuidof(IDirectXVideoMemoryConfiguration), (void**)&pVideoConfig);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> IDirectXVideoMemoryConfiguration not available"));
+ goto done;
+ }
+
+ // Notify the EVR about the format we're sending
+ DXVA2_SurfaceType surfaceType;
+ for (DWORD iTypeIndex = 0; ; iTypeIndex++) {
+ hr = pVideoConfig->GetAvailableSurfaceTypeByIndex(iTypeIndex, &surfaceType);
+ if (FAILED(hr)) {
+ hr = S_OK;
+ break;
+ }
+
+ if (surfaceType == DXVA2_SurfaceType_DecoderRenderTarget) {
+ hr = pVideoConfig->SetSurfaceType(DXVA2_SurfaceType_DecoderRenderTarget);
+ break;
+ }
+ }
+done:
+ SafeRelease(&pGetService);
+ SafeRelease(&pVideoConfig);
+ return hr;
+}
+
STDMETHODIMP CDecDXVA2::LoadDXVA2Functions()
{
// Load DXVA2 library
@@ -256,23 +369,19 @@ done:
return hr;
}
-HRESULT CDecDXVA2::CreateDXVAVideoService(IDirect3DDevice9 *pDevice, IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService)
+HRESULT CDecDXVA2::CreateDXVAVideoService(IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService)
{
HRESULT hr = S_OK;
IDirectXVideoDecoderService *pService;
- HANDLE hDevice;
- hr = pManager->OpenDeviceHandle(&hDevice);
+ hr = pManager->OpenDeviceHandle(&m_hDevice);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 10, L"-> OpenDeviceHandle failed"));
goto done;
}
- HRESULT hr2 = pManager->GetVideoService(hDevice, IID_IDirectXVideoDecoderService, (void**)&pService);
-
- // Close the device handle.
- hr = pManager->CloseDeviceHandle(hDevice);
+ HRESULT hr2 = pManager->GetVideoService(m_hDevice, IID_IDirectXVideoDecoderService, (void**)&pService);
if (FAILED(hr2)) {
DbgLog((LOG_ERROR, 10, L"-> Acquiring VideoDecoderService failed"));
@@ -366,10 +475,13 @@ done:
return E_FAIL;
}
-// ILAVDecoder
-STDMETHODIMP CDecDXVA2::Init()
+/**
+ * This function is only called in non-native mode
+ * Its responsibility is to initialize D3D, create a device and a device manager
+ * and call SetD3DDeviceManager with it.
+ */
+HRESULT CDecDXVA2::InitD3D()
{
- DbgLog((LOG_TRACE, 10, L"CDecDXVA2::Init(): Trying to open DXVA2 decoder"));
HRESULT hr = S_OK;
if (FAILED(hr = LoadDXVA2Functions())) {
@@ -425,30 +537,78 @@ STDMETHODIMP CDecDXVA2::Init()
return E_FAIL;
}
- hr = CreateDXVAVideoService(m_pD3DDev, m_pD3DDevMngr, &m_pDXVADecoderService);
+ hr = SetD3DDeviceManager(m_pD3DDevMngr);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> SetD3DDeviceManager failed with hr: %X", hr));
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+/**
+ * Called from both native and non-native mode
+ * Initialize all the common DXVA2 interfaces and device handles
+ */
+HRESULT CDecDXVA2::SetD3DDeviceManager(IDirect3DDeviceManager9 *pDevManager)
+{
+ HRESULT hr = S_OK;
+ ASSERT(pDevManager);
+
+ m_pD3DDevMngr = pDevManager;
+ hr = CreateDXVAVideoService(m_pD3DDevMngr, &m_pDXVADecoderService);
if (FAILED(hr)) {
DbgLog((LOG_TRACE, 10, L"-> Creation of DXVA2 Decoder Service failed with hr: %X", hr));
return E_FAIL;
}
- // Init the ffmpeg parts
- // This is our main software decoder, unable to fail!
- CDecAvcodec::Init();
+ // If the decoder was initialized already, check if we can use this device
+ if (m_pAVCtx) {
+ GUID input = GUID_NULL;
+ D3DFORMAT output;
+ hr = FindVideoServiceConversion(m_pAVCtx->codec_id, &input, &output);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(m_pAVCtx->codec_id)));
+ return E_FAIL;
+ }
+ }
- if (CopyFrameNV12 == NULL) {
- int cpu_flags = av_get_cpu_flags();
- if (cpu_flags & AV_CPU_FLAG_SSE4) {
- DbgLog((LOG_TRACE, 10, L"-> Using SSE4 frame copy"));
- CopyFrameNV12 = CopyFrameNV12_SSE4;
- } else if (FALSE && cpu_flags & AV_CPU_FLAG_SSE2) {
- DbgLog((LOG_TRACE, 10, L"-> Using SSE2 frame copy"));
- //CopyFrameNV12 = CopyFrameNV12_SSE2;
- } else {
- DbgLog((LOG_TRACE, 10, L"-> Using fallback frame copy"));
- CopyFrameNV12 = CopyFrameNV12_fallback;
+ return S_OK;
+}
+
+// ILAVDecoder
+STDMETHODIMP CDecDXVA2::Init()
+{
+ DbgLog((LOG_TRACE, 10, L"CDecDXVA2::Init(): Trying to open DXVA2 decoder"));
+ HRESULT hr = S_OK;
+
+ // Initialize all D3D interfaces in non-native mode
+ if (!m_bNative) {
+ hr = InitD3D();
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> D3D Initialization failed with hr: %X", hr));
+ return hr;
+ }
+
+ if (CopyFrameNV12 == NULL) {
+ int cpu_flags = av_get_cpu_flags();
+ if (cpu_flags & AV_CPU_FLAG_SSE4) {
+ DbgLog((LOG_TRACE, 10, L"-> Using SSE4 frame copy"));
+ CopyFrameNV12 = CopyFrameNV12_SSE4;
+ } else if (FALSE && cpu_flags & AV_CPU_FLAG_SSE2) {
+ DbgLog((LOG_TRACE, 10, L"-> Using SSE2 frame copy"));
+ //CopyFrameNV12 = CopyFrameNV12_SSE2;
+ } else {
+ DbgLog((LOG_TRACE, 10, L"-> Using fallback frame copy"));
+ CopyFrameNV12 = CopyFrameNV12_fallback;
+ }
}
}
+ // Init the ffmpeg parts
+ // This is our main software decoder, unable to fail!
+ CDecAvcodec::Init();
+
return S_OK;
}
@@ -459,12 +619,16 @@ STDMETHODIMP CDecDXVA2::InitDecoder(CodecID codec, const CMediaType *pmt)
DestroyDecoder(false);
- GUID input = GUID_NULL;
- D3DFORMAT output;
- hr = FindVideoServiceConversion(codec, &input, &output);
- if (FAILED(hr)) {
- DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(codec)));
- return E_FAIL;
+ // If we have a DXVA Decoder, check if its capable
+ // If we don't have one yet, it may be handed to us later, and compat is checked at that point
+ if (m_pDXVADecoderService) {
+ GUID input = GUID_NULL;
+ D3DFORMAT output;
+ hr = FindVideoServiceConversion(codec, &input, &output);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> No decoder device available that can decode codec '%S' to NV12", avcodec_get_name(codec)));
+ return E_FAIL;
+ }
}
m_bFailHWDecode = FALSE;
@@ -480,12 +644,20 @@ STDMETHODIMP CDecDXVA2::InitDecoder(CodecID codec, const CMediaType *pmt)
return E_FAIL;
}
+ m_dwSurfaceWidth = FFALIGN(m_pAVCtx->coded_width, 16);
+ m_dwSurfaceHeight = FFALIGN(m_pAVCtx->coded_height, 16);
+
return S_OK;
}
-HRESULT CDecDXVA2::CreateDXVA2Decoder()
+HRESULT CDecDXVA2::CreateDXVA2Decoder(int nSurfaces, IDirect3DSurface9 **ppSurfaces)
{
+ DbgLog((LOG_TRACE, 10, L"-> CDecDXVA2::CreateDXVA2Decoder"));
HRESULT hr = S_OK;
+ LPDIRECT3DSURFACE9 pSurfaces[DXVA2_MAX_SURFACES];
+
+ if (!m_pDXVADecoderService)
+ return E_FAIL;
DestroyDecoder(false, true);
@@ -493,22 +665,27 @@ HRESULT CDecDXVA2::CreateDXVA2Decoder()
D3DFORMAT output;
FindVideoServiceConversion(m_pAVCtx->codec_id, &input, &output);
- m_dwSurfaceWidth = FFALIGN(m_pAVCtx->coded_width, 16);
- m_dwSurfaceHeight = FFALIGN(m_pAVCtx->coded_height, 16);
-
- m_NumSurfaces = (m_pAVCtx->codec_id == CODEC_ID_H264) ? 16 + DXVA2_QUEUE_SURFACES + 2 : 2 + DXVA2_QUEUE_SURFACES + 2;
- LPDIRECT3DSURFACE9 pSurfaces[DXVA2_MAX_SURFACES];
- hr = m_pDXVADecoderService->CreateSurface(m_dwSurfaceWidth, m_dwSurfaceHeight, m_NumSurfaces - 1, output, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, pSurfaces, NULL);
- if (FAILED(hr)) {
- DbgLog((LOG_TRACE, 10, L"-> Creation of surfaces failed with hr: %X", hr));
- m_NumSurfaces = 0;
- return E_FAIL;
+ if (!nSurfaces) {
+ m_NumSurfaces = (m_pAVCtx->codec_id == CODEC_ID_H264) ? 16 + DXVA2_QUEUE_SURFACES + 2 : 2 + DXVA2_QUEUE_SURFACES + 2;
+ hr = m_pDXVADecoderService->CreateSurface(m_dwSurfaceWidth, m_dwSurfaceHeight, m_NumSurfaces - 1, output, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, pSurfaces, NULL);
+ if (FAILED(hr)) {
+ DbgLog((LOG_TRACE, 10, L"-> Creation of surfaces failed with hr: %X", hr));
+ m_NumSurfaces = 0;
+ return E_FAIL;
+ }
+ ppSurfaces = pSurfaces;
+ } else {
+ m_NumSurfaces = nSurfaces;
+ for (int i = 0; i < m_NumSurfaces; i++) {
+ ppSurfaces[i]->AddRef();
+ }
}
for (int i = 0; i < m_NumSurfaces; i++) {
- m_pSurfaces[i].d3d = pSurfaces[i];
+ m_pSurfaces[i].d3d = ppSurfaces[i];
m_pSurfaces[i].ref = 0;
m_pSurfaces[i].age = 0;
+ m_pSurfaces[i].sample = NULL;
}
DbgLog((LOG_TRACE, 10, L"-> Successfully created %d surfaces (%dx%d)", m_NumSurfaces, m_dwSurfaceWidth, m_dwSurfaceHeight));
@@ -556,7 +733,7 @@ HRESULT CDecDXVA2::CreateDXVA2Decoder()
m_DXVAVideoDecoderConfig = best_cfg;
IDirectXVideoDecoder *decoder;
- hr = m_pDXVADecoderService->CreateVideoDecoder(input, &desc, &m_DXVAVideoDecoderConfig, pSurfaces, m_NumSurfaces, &decoder);
+ hr = m_pDXVADecoderService->CreateVideoDecoder(input, &desc, &m_DXVAVideoDecoderConfig, ppSurfaces, m_NumSurfaces, &decoder);
if (FAILED(hr)) {
DbgLog((LOG_TRACE, 10, L"-> CreateVideoDecoder failed with hr: %X", hr));
return E_FAIL;
@@ -592,36 +769,66 @@ int CDecDXVA2::get_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic)
HRESULT hr = S_OK;
+ if (c->pix_fmt != PIX_FMT_DXVA2_VLD || c->profile > FF_PROFILE_H264_HIGH) {
+ DbgLog((LOG_ERROR, 10, L"DXVA2 buffer request, but not dxva2 pixfmt or unsupported profile"));
+ pDec->m_bFailHWDecode = TRUE;
+ return -1;
+ }
+
if (!pDec->m_pDecoder || FFALIGN(c->coded_width, 16) != pDec->m_dwSurfaceWidth || FFALIGN(c->coded_height, 16) != pDec->m_dwSurfaceHeight) {
- hr = pDec->CreateDXVA2Decoder();
+ if (!pDec->m_pDecoder && pDec->m_bNative) {
+ ASSERT(0);
+ } else if (!pDec->m_bNative) {
+ hr = pDec->CreateDXVA2Decoder();
+ }
if (FAILED(hr)) {
pDec->m_bFailHWDecode = TRUE;
return -1;
}
}
- if (c->pix_fmt != PIX_FMT_DXVA2_VLD || c->profile > FF_PROFILE_H264_HIGH) {
- DbgLog((LOG_ERROR, 10, L"DXVA2 buffer request, but not dxva2 pixfmt or unsupported profile"));
- pDec->m_bFailHWDecode = TRUE;
- return -1;
- }
-
pic->type = FF_BUFFER_TYPE_USER;
//pDec->m_pD3DDevMngr->TestDevice(
- int i, old, old_unused;
- for (i = 0, old = 0, old_unused = -1; i < pDec->m_NumSurfaces; i++) {
- d3d_surface_t *surface = &pDec->m_pSurfaces[i];
- if (!surface->ref && (old_unused == -1 || surface->age < pDec->m_pSurfaces[old_unused].age))
- old_unused = i;
- if (surface->age < pDec->m_pSurfaces[old].age)
- old = i;
- }
- if (old_unused == -1) {
- DbgLog((LOG_TRACE, 10, L"No free surface, using oldest"));
- i = old;
+
+ int i;
+ if (pDec->m_bNative) {
+ if (!pDec->m_pDXVA2Allocator)
+ return -1;
+
+ IMediaSample *pSample = NULL;
+ hr = pDec->m_pDXVA2Allocator->GetBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"DXVA2Allocator returned error"));
+ return -1;
+ }
+ ILAVDXVA2Sample *pLavDXVA2 = NULL;
+ hr = pSample->QueryInterface(&pLavDXVA2);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"Sample is no LAV DXVA2 sample?????"));
+ return -1;
+ }
+ i = pLavDXVA2->GetDXSurfaceId();
+ SafeRelease(&pLavDXVA2);
+
+ pDec->m_pSurfaces[i].sample = pSample;
+
+ DbgLog((LOG_TRACE, 10, L"get_dxva2_buffer on dxva2 buffer %i", i));
} else {
- i = old_unused;
+ int old, old_unused;
+ for (i = 0, old = 0, old_unused = -1; i < pDec->m_NumSurfaces; i++) {
+ d3d_surface_t *surface = &pDec->m_pSurfaces[i];
+ if (!surface->ref && (old_unused == -1 || surface->age < pDec->m_pSurfaces[old_unused].age))
+ old_unused = i;
+ if (surface->age < pDec->m_pSurfaces[old].age)
+ old = i;
+ }
+ if (old_unused == -1) {
+ DbgLog((LOG_TRACE, 10, L"No free surface, using oldest"));
+ i = old;
+ } else {
+ i = old_unused;
+ }
}
LPDIRECT3DSURFACE9 pSurface = pDec->m_pSurfaces[i].d3d;
@@ -643,8 +850,13 @@ void CDecDXVA2::release_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic)
LPDIRECT3DSURFACE9 pSurface = (LPDIRECT3DSURFACE9)pic->data[3];
for (int i = 0; i < pDec->m_NumSurfaces; i++) {
- if (pDec->m_pSurfaces[i].d3d == pSurface)
+ if (pDec->m_pSurfaces[i].d3d == pSurface) {
pDec->m_pSurfaces[i].ref--;
+ if (pDec->m_pSurfaces[i].ref == 0) {
+ DbgLog((LOG_TRACE, 10, L"release_dxva2_buffer on dxva2 buffer %d", i));
+ SafeRelease(&pDec->m_pSurfaces[i].sample);
+ }
+ }
}
memset(pic->data, 0, sizeof(pic->data));
@@ -693,6 +905,7 @@ STDMETHODIMP CDecDXVA2::Flush()
for (int i = 0; i < m_NumSurfaces; i++) {
d3d_surface_t *s = &m_pSurfaces[i];
+ SafeRelease(&s->sample);
s->ref = 0;
//s->age = 0;
}
@@ -726,18 +939,22 @@ STDMETHODIMP CDecDXVA2::EndOfStream()
HRESULT CDecDXVA2::HandleDXVA2Frame(LAVFrame *pFrame)
{
- LAVFrame *pQueuedFrame = m_FrameQueue[m_FrameQueuePosition];
- m_FrameQueue[m_FrameQueuePosition] = pFrame;
+ if (m_bNative) {
+ DeliverDXVA2Frame(pFrame);
+ } else {
+ LAVFrame *pQueuedFrame = m_FrameQueue[m_FrameQueuePosition];
+ m_FrameQueue[m_FrameQueuePosition] = pFrame;
- d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]);
- s->ref++;
+ d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]);
+ s->ref++;
- m_FrameQueuePosition = (m_FrameQueuePosition + 1) % DXVA2_QUEUE_SURFACES;
+ m_FrameQueuePosition = (m_FrameQueuePosition + 1) % DXVA2_QUEUE_SURFACES;
- if (pQueuedFrame) {
- s = FindSurface((LPDIRECT3DSURFACE9)pQueuedFrame->data[3]);
- s->ref--;
- DeliverDXVA2Frame(pQueuedFrame);
+ if (pQueuedFrame) {
+ s = FindSurface((LPDIRECT3DSURFACE9)pQueuedFrame->data[3]);
+ s->ref--;
+ DeliverDXVA2Frame(pQueuedFrame);
+ }
}
return S_OK;
@@ -745,10 +962,20 @@ HRESULT CDecDXVA2::HandleDXVA2Frame(LAVFrame *pFrame)
HRESULT CDecDXVA2::DeliverDXVA2Frame(LAVFrame *pFrame)
{
- if (CopyFrame(pFrame))
+ if (m_bNative) {
+ d3d_surface_t *s = FindSurface((LPDIRECT3DSURFACE9)pFrame->data[3]);
+
+ ASSERT(s->sample);
+ pFrame->data[0] = (uint8_t *)s->sample;
+
+ pFrame->format = LAVPixFmt_DXVA2;
Deliver(pFrame);
- else
- ReleaseFrame(&pFrame);
+ } else {
+ if (CopyFrame(pFrame))
+ Deliver(pFrame);
+ else
+ ReleaseFrame(&pFrame);
+ }
return S_OK;
}
@@ -756,8 +983,12 @@ HRESULT CDecDXVA2::DeliverDXVA2Frame(LAVFrame *pFrame)
STDMETHODIMP CDecDXVA2::GetPixelFormat(LAVPixelFormat *pPix, int *pBpp)
{
// Output is always NV12
- if (pPix)
- *pPix = LAVPixFmt_NV12;
+ if (pPix) {
+ if (m_bNative)
+ *pPix = LAVPixFmt_DXVA2;
+ else
+ *pPix = LAVPixFmt_NV12;
+ }
if (pBpp)
*pBpp = 8;
return S_OK;
diff --git a/decoder/LAVVideo/decoders/dxva2dec.h b/decoder/LAVVideo/decoders/dxva2dec.h
index 63f622a9..7bc56b46 100644
--- a/decoder/LAVVideo/decoders/dxva2dec.h
+++ b/decoder/LAVVideo/decoders/dxva2dec.h
@@ -30,8 +30,11 @@ typedef struct {
LPDIRECT3DSURFACE9 d3d;
uint64_t age;
long ref;
+ IMediaSample *sample;
} d3d_surface_t;
+class CDXVA2SurfaceAllocator;
+
class CDecDXVA2 : public CDecAvcodec
{
public:
@@ -44,9 +47,14 @@ public:
STDMETHODIMP Flush();
STDMETHODIMP EndOfStream();
+ STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc);
+ STDMETHODIMP PostConnect(IPin *pPin);
+
// CDecBase
STDMETHODIMP Init();
+ HRESULT SetNativeMode(BOOL bNative) { m_bNative = bNative; return S_OK; }
+
protected:
HRESULT AdditionaDecoderInit();
HRESULT PostDecode();
@@ -56,20 +64,27 @@ protected:
bool CopyFrame(LAVFrame *pFrame);
private:
+ HRESULT InitD3D();
STDMETHODIMP DestroyDecoder(bool bFull, bool bNoAVCodec = false);
STDMETHODIMP LoadDXVA2Functions();
HRESULT CreateD3DDeviceManager(IDirect3DDevice9 *pDevice, UINT *pReset, IDirect3DDeviceManager9 **ppManager);
- HRESULT CreateDXVAVideoService(IDirect3DDevice9 *pDevice, IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService);
+ HRESULT CreateDXVAVideoService(IDirect3DDeviceManager9 *pManager, IDirectXVideoDecoderService **ppService);
HRESULT FindVideoServiceConversion(CodecID codec, GUID *input, D3DFORMAT *output);
- HRESULT CreateDXVA2Decoder();
+ HRESULT CreateDXVA2Decoder(int nSurfaces = 0, IDirect3DSurface9 **ppSurfaces = NULL);
+ HRESULT SetD3DDeviceManager(IDirect3DDeviceManager9 *pDevManager);
+ HRESULT DXVA2NotifyEVR();
static int get_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic);
static void release_dxva2_buffer(struct AVCodecContext *c, AVFrame *pic);
d3d_surface_t *FindSurface(LPDIRECT3DSURFACE9 pSurface);
private:
+ friend class CDXVA2SurfaceAllocator;
+ BOOL m_bNative;
+ CDXVA2SurfaceAllocator *m_pDXVA2Allocator;
+
struct {
HMODULE dxva2lib;
pCreateDeviceManager9 *createDeviceManager;
@@ -79,6 +94,7 @@ private:
IDirect3DDevice9 *m_pD3DDev;
IDirect3DDeviceManager9 *m_pD3DDevMngr;
UINT m_pD3DResetToken;
+ HANDLE m_hDevice;
IDirectXVideoDecoderService *m_pDXVADecoderService;
IDirectXVideoDecoder *m_pDecoder;