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>2017-07-05 18:33:14 +0300
committerHendrik Leppkes <h.leppkes@gmail.com>2017-08-11 17:22:40 +0300
commit465f06e157379521bcda5a8220e658d9a5a41f14 (patch)
treec55eda096db625ca337907d75c743621f9dec0b8 /decoder
parentcd936764aa3915c858c824cf48301202bb69126e (diff)
Implement D3D11 hardware decoding mode
Diffstat (limited to 'decoder')
-rw-r--r--decoder/LAVVideo/DecodeManager.cpp4
-rw-r--r--decoder/LAVVideo/LAVPixFmtConverter.cpp6
-rw-r--r--decoder/LAVVideo/LAVVideo.cpp22
-rw-r--r--decoder/LAVVideo/LAVVideo.vcxproj5
-rw-r--r--decoder/LAVVideo/LAVVideo.vcxproj.filters21
-rw-r--r--decoder/LAVVideo/LAVVideoSettings.h1
-rw-r--r--decoder/LAVVideo/VideoSettingsProp.cpp2
-rw-r--r--decoder/LAVVideo/decoders/ILAVDecoder.h2
-rw-r--r--decoder/LAVVideo/decoders/avcodec.cpp5
-rw-r--r--decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.cpp215
-rw-r--r--decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.h75
-rw-r--r--decoder/LAVVideo/decoders/d3d11/ID3DVideoMemoryConfiguration.h48
-rw-r--r--decoder/LAVVideo/decoders/d3d11va.cpp767
-rw-r--r--decoder/LAVVideo/decoders/d3d11va.h95
-rw-r--r--decoder/LAVVideo/decoders/pixfmt.cpp2
-rw-r--r--decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp4
16 files changed, 1264 insertions, 10 deletions
diff --git a/decoder/LAVVideo/DecodeManager.cpp b/decoder/LAVVideo/DecodeManager.cpp
index ccea0dce..4ea51b0b 100644
--- a/decoder/LAVVideo/DecodeManager.cpp
+++ b/decoder/LAVVideo/DecodeManager.cpp
@@ -69,6 +69,8 @@ ILAVDecoder * CDecodeManager::CreateHWAccelDecoder(LAVHWAccel hwAccel)
pDecoder = CreateDecoderDXVA2();
else if (hwAccel == HWAccel_DXVA2Native)
pDecoder = CreateDecoderDXVA2Native();
+ else if (hwAccel == HWAccel_D3D11)
+ pDecoder = CreateDecoderD3D11();
return pDecoder;
}
@@ -172,7 +174,7 @@ STDMETHODIMP CDecodeManager::Decode(IMediaSample *pSample)
m_bHWDecoderFailed = TRUE;
// If we're disabling DXVA2 Native decoding, we need to release resources now
- if (wcscmp(m_pDecoder->GetDecoderName(), L"dxva2n") == 0) {
+ if (wcscmp(m_pDecoder->GetDecoderName(), L"dxva2n") == 0 || wcscmp(m_pDecoder->GetDecoderName(), L"d3d11 native") == 0) {
m_pLAVVideo->ReleaseAllDXVAResources();
m_pLAVVideo->GetOutputPin()->GetConnected()->BeginFlush();
m_pLAVVideo->GetOutputPin()->GetConnected()->EndFlush();
diff --git a/decoder/LAVVideo/LAVPixFmtConverter.cpp b/decoder/LAVVideo/LAVPixFmtConverter.cpp
index 458c3536..c75b73b3 100644
--- a/decoder/LAVVideo/LAVPixFmtConverter.cpp
+++ b/decoder/LAVVideo/LAVPixFmtConverter.cpp
@@ -106,6 +106,10 @@ static LAV_INOUT_PIXFMT_MAP lav_pixfmt_map[] = {
{ LAVPixFmt_DXVA2, 8, { PIXOUT_420_8, PIXOUT_420_10, PIXOUT_420_16, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8, PIXOUT_RGB_16 } },
{ LAVPixFmt_DXVA2, 10, { PIXOUT_420_10, PIXOUT_420_16, PIXOUT_420_8, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_RGB_16, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8 } },
{ LAVPixFmt_DXVA2, 16, { PIXOUT_420_16, PIXOUT_420_10, PIXOUT_420_8, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_RGB_16, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8 } },
+
+ { LAVPixFmt_D3D11, 8, { PIXOUT_420_8, PIXOUT_420_10, PIXOUT_420_16, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8, PIXOUT_RGB_16 } },
+ { LAVPixFmt_D3D11, 10, { PIXOUT_420_10, PIXOUT_420_16, PIXOUT_420_8, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_RGB_16, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8 } },
+ { LAVPixFmt_D3D11, 16, { PIXOUT_420_16, PIXOUT_420_10, PIXOUT_420_8, PIXOUT_422_16, PIXOUT_422_10, PIXOUT_422_8, PIXOUT_RGB_8, PIXOUT_RGB_16, PIXOUT_444_16, PIXOUT_444_10, PIXOUT_444_8 } },
};
LAVOutPixFmtDesc lav_pixfmt_desc[] = {
@@ -169,7 +173,7 @@ LAVOutPixFmts CLAVPixFmtConverter::GetOutputBySubtype(const GUID *guid)
static bool IsDXVAPixFmt(LAVPixelFormat inputFormat, LAVOutPixFmts outputFormat, int bpp)
{
- if (inputFormat != LAVPixFmt_DXVA2)
+ if (inputFormat != LAVPixFmt_DXVA2 && inputFormat != LAVPixFmt_D3D11)
return false;
if (bpp == 8 && outputFormat == LAVOutPixFmt_NV12)
diff --git a/decoder/LAVVideo/LAVVideo.cpp b/decoder/LAVVideo/LAVVideo.cpp
index b905a765..97ccee30 100644
--- a/decoder/LAVVideo/LAVVideo.cpp
+++ b/decoder/LAVVideo/LAVVideo.cpp
@@ -818,6 +818,10 @@ HRESULT CLAVVideo::ReleaseLastSequenceFrame()
SafeRelease(&pSample);
SafeRelease(&pSurface);
}
+ else if (m_pLastSequenceFrame && m_pLastSequenceFrame->format == LAVPixFmt_D3D11)
+ {
+ // TODO D3D11
+ }
ReleaseFrame(&m_pLastSequenceFrame);
return S_OK;
@@ -1578,7 +1582,7 @@ STDMETHODIMP CLAVVideo::Deliver(LAVFrame *pFrame)
// Only perform filtering if we have to.
// DXVA Native generally can't be filtered, and the only filtering we currently support is software deinterlacing
- if ( pFrame->format == LAVPixFmt_DXVA2
+ if ( pFrame->format == LAVPixFmt_DXVA2 || pFrame->format == LAVPixFmt_D3D11
|| !(m_Decoder.IsInterlaced(FALSE) && m_settings.SWDeintMode != SWDeintMode_None)
|| pFrame->flags & LAV_FRAME_FLAG_REDRAW) {
return DeliverToRenderer(pFrame);
@@ -1600,7 +1604,7 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
if (!(pFrame->flags & LAV_FRAME_FLAG_REDRAW)) {
// Release the old End-of-Sequence frame, this ensures any "normal" frame will clear the stored EOS frame
- if (pFrame->format != LAVPixFmt_DXVA2) {
+ if (pFrame->format != LAVPixFmt_DXVA2 && pFrame->format != LAVPixFmt_D3D11) {
ReleaseFrame(&m_pLastSequenceFrame);
if ((pFrame->flags & LAV_FRAME_FLAG_END_OF_SEQUENCE || m_bInDVDMenu)) {
if (pFrame->direct) {
@@ -1612,7 +1616,7 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
}
CopyLAVFrame(pFrame, &m_pLastSequenceFrame);
}
- } else {
+ } else if (pFrame->format == LAVPixFmt_DXVA2) {
if ((pFrame->flags & LAV_FRAME_FLAG_END_OF_SEQUENCE || m_bInDVDMenu)) {
if (!m_pLastSequenceFrame) {
hr = AllocateFrame(&m_pLastSequenceFrame);
@@ -1653,6 +1657,10 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
}
}
}
+ else if (pFrame->format == LAVPixFmt_D3D11)
+ {
+ // TODO D3D11
+ }
}
if (m_bFlushing) {
@@ -1759,7 +1767,7 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
if (avgDuration == 0)
avgDuration = AV_NOPTS_VALUE;
- if (pFrame->format == LAVPixFmt_DXVA2) {
+ if (pFrame->format == LAVPixFmt_DXVA2 || pFrame->format == LAVPixFmt_D3D11) {
pSampleOut = (IMediaSample *)pFrame->data[0];
// Addref the sample if we need to. If its coming from the decoder, it should be addref'ed,
// but if its a copy from the subtitle engine, then it should not be addref'ed.
@@ -1790,7 +1798,7 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
}
}
- if (pFrame->format != LAVPixFmt_DXVA2) {
+ if (pFrame->format != LAVPixFmt_DXVA2 && pFrame->format != LAVPixFmt_D3D11) {
long required = m_PixFmtConverter.GetImageSize(pBIH->biWidth, abs(pBIH->biHeight));
long lSampleSize = pSampleOut->GetSize();
@@ -1875,7 +1883,7 @@ HRESULT CLAVVideo::DeliverToRenderer(LAVFrame *pFrame)
pSampleOut->SetMediaType(sendmt);
DeleteMediaType(sendmt);
m_bSendMediaType = FALSE;
- if (pFrame->format == LAVPixFmt_DXVA2)
+ if (pFrame->format == LAVPixFmt_DXVA2 || pFrame->format == LAVPixFmt_D3D11)
bSizeChanged = TRUE;
}
@@ -1992,6 +2000,8 @@ HRESULT CLAVVideo::RedrawStillImage()
SafeRelease(&pSample);
}
return hr;
+ } else if (m_pLastSequenceFrame->format == LAVPixFmt_D3D11) {
+ // TODO D3D11
} else {
LAVFrame *pFrame = nullptr;
HRESULT hr = CopyLAVFrame(m_pLastSequenceFrame, &pFrame);
diff --git a/decoder/LAVVideo/LAVVideo.vcxproj b/decoder/LAVVideo/LAVVideo.vcxproj
index 476dc466..0c35a3b9 100644
--- a/decoder/LAVVideo/LAVVideo.vcxproj
+++ b/decoder/LAVVideo/LAVVideo.vcxproj
@@ -102,6 +102,8 @@
<ItemGroup>
<ClCompile Include="decoders\avcodec.cpp" />
<ClCompile Include="decoders\cuvid.cpp" />
+ <ClCompile Include="decoders\d3d11va.cpp" />
+ <ClCompile Include="decoders\d3d11\D3D11SurfaceAllocator.cpp" />
<ClCompile Include="decoders\dxva2dec.cpp" />
<ClCompile Include="decoders\dxva2\DXVA2SurfaceAllocator.cpp" />
<ClCompile Include="decoders\dxva2\dxva_common.cpp" />
@@ -148,6 +150,9 @@
<ClInclude Include="..\..\common\includes\SubRenderIntf.h" />
<ClInclude Include="decoders\avcodec.h" />
<ClInclude Include="decoders\cuvid.h" />
+ <ClInclude Include="decoders\d3d11va.h" />
+ <ClInclude Include="decoders\d3d11\D3D11SurfaceAllocator.h" />
+ <ClInclude Include="decoders\d3d11\ID3DVideoMemoryConfiguration.h" />
<ClInclude Include="decoders\DecBase.h" />
<ClInclude Include="decoders\dxva2dec.h" />
<ClInclude Include="decoders\dxva2\DXVA2SurfaceAllocator.h" />
diff --git a/decoder/LAVVideo/LAVVideo.vcxproj.filters b/decoder/LAVVideo/LAVVideo.vcxproj.filters
index 586de304..a334d4f0 100644
--- a/decoder/LAVVideo/LAVVideo.vcxproj.filters
+++ b/decoder/LAVVideo/LAVVideo.vcxproj.filters
@@ -46,6 +46,12 @@
<Filter Include="Source Files\subtitles\blend">
<UniqueIdentifier>{313fee8a-af36-434b-9ac0-808a14d70bb0}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\decoders\d3d11">
+ <UniqueIdentifier>{aa256837-6553-43d3-bdf9-bce2e149fb21}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\decoders\d3d11">
+ <UniqueIdentifier>{5bef8a26-ba3d-4eb9-aa69-0bfaf1c6a433}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@@ -168,6 +174,12 @@
<ClCompile Include="decoders\dxva2\dxva_common.cpp">
<Filter>Source Files\decoders\dxva2</Filter>
</ClCompile>
+ <ClCompile Include="decoders\d3d11va.cpp">
+ <Filter>Source Files\decoders</Filter>
+ </ClCompile>
+ <ClCompile Include="decoders\d3d11\D3D11SurfaceAllocator.cpp">
+ <Filter>Source Files\decoders\d3d11</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@@ -275,6 +287,15 @@
<ClInclude Include="decoders\dxva2\dxva_common.h">
<Filter>Header Files\decoders\dxva2</Filter>
</ClInclude>
+ <ClInclude Include="decoders\d3d11va.h">
+ <Filter>Header Files\decoders</Filter>
+ </ClInclude>
+ <ClInclude Include="decoders\d3d11\ID3DVideoMemoryConfiguration.h">
+ <Filter>Header Files\decoders\d3d11</Filter>
+ </ClInclude>
+ <ClInclude Include="decoders\d3d11\D3D11SurfaceAllocator.h">
+ <Filter>Header Files\decoders\d3d11</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="LAVVideo.rc">
diff --git a/decoder/LAVVideo/LAVVideoSettings.h b/decoder/LAVVideo/LAVVideoSettings.h
index 60afb96d..8a5a93a8 100644
--- a/decoder/LAVVideo/LAVVideoSettings.h
+++ b/decoder/LAVVideo/LAVVideoSettings.h
@@ -120,6 +120,7 @@ typedef enum LAVHWAccel {
HWAccel_DXVA2,
HWAccel_DXVA2CopyBack = HWAccel_DXVA2,
HWAccel_DXVA2Native,
+ HWAccel_D3D11,
HWAccel_NB, // Number of HWAccels
} LAVHWAccel;
diff --git a/decoder/LAVVideo/VideoSettingsProp.cpp b/decoder/LAVVideo/VideoSettingsProp.cpp
index b85439ee..21765472 100644
--- a/decoder/LAVVideo/VideoSettingsProp.cpp
+++ b/decoder/LAVVideo/VideoSettingsProp.cpp
@@ -212,11 +212,13 @@ HRESULT CLAVVideoSettingsProp::OnActivate()
WCHAR hwAccelQuickSync[] = L"Intel\xae QuickSync";
WCHAR hwAccelDXVA2CB[] = L"DXVA2 (copy-back)";
WCHAR hwAccelDXVA2N[] = L"DXVA2 (native)";
+ WCHAR hwAccelD3D11[] = L"D3D11";
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)hwAccelDXVA2CB);
SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelDXVA2N);
+ SendDlgItemMessage(m_Dlg, IDC_HWACCEL, CB_ADDSTRING, 0, (LPARAM)hwAccelD3D11);
// Init the fieldorder Combo Box
SendDlgItemMessage(m_Dlg, IDC_DEINT_FIELDORDER, CB_RESETCONTENT, 0, 0);
diff --git a/decoder/LAVVideo/decoders/ILAVDecoder.h b/decoder/LAVVideo/decoders/ILAVDecoder.h
index 8791db15..2e47430d 100644
--- a/decoder/LAVVideo/decoders/ILAVDecoder.h
+++ b/decoder/LAVVideo/decoders/ILAVDecoder.h
@@ -54,6 +54,7 @@ typedef enum LAVPixelFormat {
LAVPixFmt_RGB48, ///< RGB48, in RGB order (16-bit per pixel)
LAVPixFmt_DXVA2, ///< DXVA2 Surface
+ LAVPixFmt_D3D11, ///< D3D11 Surface
LAVPixFmt_NB, ///< number of formats
} LAVPixelFormat;
@@ -432,6 +433,7 @@ ILAVDecoder *CreateDecoderCUVID();
ILAVDecoder *CreateDecoderQuickSync();
ILAVDecoder *CreateDecoderDXVA2();
ILAVDecoder *CreateDecoderDXVA2Native();
+ILAVDecoder *CreateDecoderD3D11();
ILAVDecoder *CreateDecoderMSDKMVC();
HRESULT VerifyD3D9Device(DWORD & dwIndex, DWORD dwDeviceId);
diff --git a/decoder/LAVVideo/decoders/avcodec.cpp b/decoder/LAVVideo/decoders/avcodec.cpp
index eaf02c32..9655507c 100644
--- a/decoder/LAVVideo/decoders/avcodec.cpp
+++ b/decoder/LAVVideo/decoders/avcodec.cpp
@@ -239,6 +239,7 @@ static struct PixelFormatMapping {
{ AV_PIX_FMT_P016LE, LAVPixFmt_P016, FALSE, 16 },
{ AV_PIX_FMT_DXVA2_VLD, LAVPixFmt_DXVA2, FALSE },
+ { AV_PIX_FMT_D3D11, LAVPixFmt_D3D11, FALSE },
};
static AVCodecID ff_interlace_capable[] = {
@@ -1127,13 +1128,15 @@ send_packet:
if (pOutFrame->format == LAVPixFmt_DXVA2) {
pOutFrame->data[0] = m_pFrame->data[4];
HandleDXVA2Frame(pOutFrame);
+ } else if (pOutFrame->format == LAVPixFmt_D3D11) {
+ HandleDXVA2Frame(pOutFrame);
} else {
Deliver(pOutFrame);
}
if (bEndOfSequence) {
bEndOfSequence = FALSE;
- if (pOutFrame->format == LAVPixFmt_DXVA2) {
+ if (pOutFrame->format == LAVPixFmt_DXVA2 || pOutFrame->format == LAVPixFmt_D3D11) {
HandleDXVA2Frame(m_pCallback->GetFlushFrame());
} else {
Deliver(m_pCallback->GetFlushFrame());
diff --git a/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.cpp b/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.cpp
new file mode 100644
index 00000000..5d97d555
--- /dev/null
+++ b/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.cpp
@@ -0,0 +1,215 @@
+/*
+* Copyright (C) 2017 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 "D3D11SurfaceAllocator.h"
+#include "decoders/d3d11va.h"
+
+extern "C" {
+#include "libavutil/hwcontext.h"
+}
+
+CD3D11MediaSample::CD3D11MediaSample(CD3D11SurfaceAllocator *pAllocator, AVFrame *pFrame, HRESULT *phr)
+ : CMediaSampleSideData(NAME("CD3D11MediaSample"), (CBaseAllocator*)pAllocator, phr, nullptr, 0)
+ , m_pFrame(pFrame)
+{
+ ASSERT(m_pFrame && m_pFrame->format == AV_PIX_FMT_D3D11);
+}
+
+CD3D11MediaSample::~CD3D11MediaSample()
+{
+ av_frame_free(&m_pFrame);
+}
+
+//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 CD3D11MediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)
+{
+ CheckPointer(ppv, E_POINTER);
+ ValidateReadWritePtr(ppv, sizeof(PVOID));
+
+ if (riid == __uuidof(IMediaSampleD3D11)) {
+ return GetInterface((IMediaSampleD3D11 *) this, ppv);
+ }
+ else {
+ return __super::QueryInterface(riid, ppv);
+ }
+}
+
+STDMETHODIMP_(ULONG) CD3D11MediaSample::AddRef()
+{
+ return __super::AddRef();
+}
+
+STDMETHODIMP_(ULONG) CD3D11MediaSample::Release()
+{
+ // Return a temporary variable for thread safety.
+ ULONG cRef = __super::Release();
+ return cRef;
+}
+
+STDMETHODIMP CD3D11MediaSample::GetD3D11Texture(int nView, ID3D11Texture2D **ppTexture, UINT *pArraySlice)
+{
+ CheckPointer(ppTexture, E_POINTER);
+ CheckPointer(pArraySlice, E_POINTER);
+
+ // only view 0 is implemented at this time
+ if (nView != 0)
+ return E_INVALIDARG;
+
+ if (m_pFrame)
+ {
+ *ppTexture = (ID3D11Texture2D *)m_pFrame->data[0];
+ *pArraySlice = (intptr_t)m_pFrame->data[1];
+
+ (*ppTexture)->AddRef();
+
+ return S_OK;
+ }
+
+ return E_FAIL;
+}
+
+static void bufref_release_sample(void *opaque, uint8_t *data)
+{
+ CD3D11MediaSample *pSample = (CD3D11MediaSample *)opaque;
+ pSample->Release();
+}
+
+STDMETHODIMP CD3D11MediaSample::GetAVFrameBuffer(AVFrame *pFrame)
+{
+ CheckPointer(pFrame, E_POINTER);
+
+ // reference bufs
+ for (int i = 0; i < AV_NUM_DATA_POINTERS; i++)
+ {
+ // copy existing refs
+ if (m_pFrame->buf[i])
+ {
+ pFrame->buf[i] = av_buffer_ref(m_pFrame->buf[i]);
+ if (pFrame->buf[i] == 0)
+ return E_OUTOFMEMORY;
+ }
+ else
+ {
+ // and add a ref to this sample
+ pFrame->buf[i] = av_buffer_create((uint8_t*)this, 1, bufref_release_sample, this, 0);
+ if (pFrame->buf[i] == 0)
+ return E_OUTOFMEMORY;
+
+ AddRef();
+ break;
+ }
+ }
+
+ // ref the hwframes ctx
+ pFrame->hw_frames_ctx = av_buffer_ref(m_pFrame->hw_frames_ctx);
+
+ // copy data into the new frame
+ pFrame->data[0] = m_pFrame->data[0];
+ pFrame->data[1] = m_pFrame->data[1];
+ pFrame->data[3] = (uint8_t *)this;
+
+ pFrame->format = AV_PIX_FMT_D3D11;
+
+ return S_OK;
+}
+
+CD3D11SurfaceAllocator::CD3D11SurfaceAllocator(CDecD3D11 *pDec, HRESULT *phr)
+ : CBaseAllocator(NAME("CD3D11SurfaceAllocator"), nullptr, phr)
+ , m_pDec(pDec)
+{
+}
+
+CD3D11SurfaceAllocator::~CD3D11SurfaceAllocator()
+{
+}
+
+HRESULT CD3D11SurfaceAllocator::Alloc(void)
+{
+ DbgLog((LOG_TRACE, 10, L"CD3D11SurfaceAllocator::Alloc()"));
+ HRESULT hr = S_OK;
+
+ CAutoLock cObjectLock(this);
+
+ if (m_pDec == nullptr)
+ return E_FAIL;
+
+ hr = __super::Alloc();
+ if (FAILED(hr))
+ return hr;
+
+ // free old resources
+ //m_pDec->FlushFromAllocator();
+ Free();
+
+ // get the frames context from the decoder
+ AVBufferRef *pDecoderFramesCtx = m_pDec->m_pFramesCtx;
+ if (pDecoderFramesCtx == nullptr)
+ return S_FALSE;
+
+ m_pFramesCtx = av_buffer_ref(pDecoderFramesCtx);
+ if (m_pFramesCtx == nullptr)
+ return E_FAIL;
+
+ // create samples
+ for (int i = 0; i < m_lCount; i++)
+ {
+ AVFrame *pFrame = av_frame_alloc();
+ int ret = av_hwframe_get_buffer(m_pFramesCtx, pFrame, 0);
+ if (ret < 0)
+ {
+ av_frame_free(&pFrame);
+ Free();
+ return E_FAIL;
+ }
+
+ CD3D11MediaSample *pSample = new CD3D11MediaSample(this, pFrame, &hr);
+ if (pSample == nullptr || FAILED(hr))
+ {
+ delete pSample;
+ Free();
+ return E_FAIL;
+ }
+
+ m_lFree.Add(pSample);
+ }
+
+ m_lAllocated = m_lCount;
+ m_bChanged = FALSE;
+
+ return S_OK;
+}
+
+void CD3D11SurfaceAllocator::Free(void)
+{
+ CAutoLock lock(this);
+ CMediaSample *pSample = nullptr;
+
+ do {
+ pSample = m_lFree.RemoveHead();
+ if (pSample) {
+ delete pSample;
+ }
+ } while (pSample);
+
+ m_lAllocated = 0;
+ av_buffer_unref(&m_pFramesCtx);
+}
diff --git a/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.h b/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.h
new file mode 100644
index 00000000..766e53c4
--- /dev/null
+++ b/decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.h
@@ -0,0 +1,75 @@
+/*
+* Copyright (C) 2017 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
+
+#include <d3d11.h>
+
+#include "MediaSampleSideData.h"
+#include "ID3DVideoMemoryConfiguration.h"
+
+class CDecD3D11;
+
+class CD3D11MediaSample : public CMediaSampleSideData, public IMediaSampleD3D11
+{
+ friend class CD3D11SurfaceAllocator;
+
+public:
+ CD3D11MediaSample(CD3D11SurfaceAllocator *pAllocator, AVFrame *pFrame, HRESULT *phr);
+ virtual ~CD3D11MediaSample();
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IMediaSample
+ STDMETHODIMP GetPointer(BYTE **ppBuffer) { return E_NOTIMPL; }
+
+ // IMediaSampleD3D11
+ STDMETHODIMP GetD3D11Texture(int nView, ID3D11Texture2D **ppTexture, UINT *pArraySlice);
+
+ // LAV Interface
+ STDMETHODIMP GetAVFrameBuffer(AVFrame *pFrame);
+
+private:
+ AVFrame *m_pFrame = nullptr;
+};
+
+
+class CD3D11SurfaceAllocator : public CBaseAllocator
+{
+public:
+ CD3D11SurfaceAllocator(CDecD3D11 *pDec, HRESULT* phr);
+ virtual ~CD3D11SurfaceAllocator();
+
+ STDMETHODIMP_(BOOL) DecommitInProgress() { CAutoLock cal(this); return m_bDecommitInProgress; }
+ STDMETHODIMP_(BOOL) IsCommited() { CAutoLock cal(this); return m_bCommitted; }
+
+ // LAV interface
+ STDMETHODIMP_(void) DecoderDestruct() { CAutoLock cal(this); m_pDec = nullptr; }
+
+protected:
+ virtual void Free(void);
+ virtual HRESULT Alloc(void);
+
+private:
+ CDecD3D11 *m_pDec = nullptr;
+ AVBufferRef *m_pFramesCtx = nullptr;
+};
diff --git a/decoder/LAVVideo/decoders/d3d11/ID3DVideoMemoryConfiguration.h b/decoder/LAVVideo/decoders/d3d11/ID3DVideoMemoryConfiguration.h
new file mode 100644
index 00000000..1e0ddc48
--- /dev/null
+++ b/decoder/LAVVideo/decoders/d3d11/ID3DVideoMemoryConfiguration.h
@@ -0,0 +1,48 @@
+// -----------------------------------------------------------------
+// ID3DVideoMemoryConfiguration interface and data structure definitions
+// -----------------------------------------------------------------
+#pragma once
+
+// -----------------------------------------------------------------
+// Control D3D11 Hardware Decoding between decoder and renderer
+// -----------------------------------------------------------------
+// A video renderer can implement this interface on its input pin
+// to signal to a decoder that its capable of accepting D3D11 texture
+// samples directly, without copying to system memory.
+//
+// The decoder will create the D3D11 device and a mutex to protect it,
+// and share it with the renderer in this interface.
+//
+// To facilitate dynamic switching of the adapter used for decoding, the
+// renderer should disconnect the decoder and re-connect it. At that
+// point the decoder should query GetD3D11AdapterIndex() again and
+// create a new decoder on the new device, as appropriate.
+interface __declspec(uuid("2BB66002-46B7-4F13-9036-7053328742BE")) ID3D11DecoderConfiguration : public IUnknown
+{
+ // Set the surface format the decoder is going to send.
+ // If the renderer is not ready to accept this format, an error will be returned.
+ virtual HRESULT STDMETHODCALLTYPE ActivateD3D11Decoding(ID3D11Device *pDevice, ID3D11DeviceContext *pContext, HANDLE hMutex, UINT nFlags) = 0;
+
+ // Get the currently preferred D3D11 adapter index (to be used with IDXGIFactory1::EnumAdapters1)
+ virtual UINT STDMETHODCALLTYPE GetD3D11AdapterIndex() = 0;
+};
+
+// -----------------------------------------------------------------
+// Media Sample to hold a D3D11 texture
+// -----------------------------------------------------------------
+// D3D11 textures used for decoding are typically array-textures,
+// a single ID3D11Texture2D object containing an array of textures
+// individually addressable by the ArraySlice index.
+//
+// The texture lifetime is bound to the media samples lifetime. The
+// media sample can only be released when the texture is no longer in
+// use, otherwise the texture will be re-used by the decoder.
+//
+// The texture is AddRef'ed when retrieved through this interface,
+// and should be Released when no longer needed.
+interface __declspec(uuid("BC8753F5-0AC8-4806-8E5F-A12B2AFE153E")) IMediaSampleD3D11 : public IUnknown
+{
+ // Get the D3D11 texture for the specified view.
+ // 2D images with only one view always use view 0. For 3D, view 0 specifies the base view, view 1 the extension view.
+ virtual HRESULT STDMETHODCALLTYPE GetD3D11Texture(int nView, ID3D11Texture2D **ppTexture, UINT *pArraySlice) = 0;
+};
diff --git a/decoder/LAVVideo/decoders/d3d11va.cpp b/decoder/LAVVideo/decoders/d3d11va.cpp
new file mode 100644
index 00000000..a1dda427
--- /dev/null
+++ b/decoder/LAVVideo/decoders/d3d11va.cpp
@@ -0,0 +1,767 @@
+/*
+* Copyright (C) 2017 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 "d3d11va.h"
+#include "d3d11/ID3DVideoMemoryConfiguration.h"
+#include "dxva2/dxva_common.h"
+
+ILAVDecoder *CreateDecoderD3D11()
+{
+ return new CDecD3D11();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// D3D11 decoder implementation
+////////////////////////////////////////////////////////////////////////////////
+
+CDecD3D11::CDecD3D11(void)
+ : CDecAvcodec()
+{
+}
+
+
+CDecD3D11::~CDecD3D11(void)
+{
+ DestroyDecoder(true);
+
+ if (m_pAllocator)
+ m_pAllocator->DecoderDestruct();
+ SafeRelease(&m_pAllocator);
+}
+
+STDMETHODIMP CDecD3D11::DestroyDecoder(bool bFull, bool bNoAVCodec)
+{
+ if (m_pOutputViews)
+ {
+ for (int i = 0; i < m_nOutputViews; i++)
+ {
+ SafeRelease(&m_pOutputViews[i]);
+ }
+ av_freep(&m_pOutputViews);
+ m_nOutputViews = 0;
+ }
+
+ SafeRelease(&m_pDecoder);
+ av_buffer_unref(&m_pFramesCtx);
+
+ if (!bNoAVCodec) {
+ CDecAvcodec::DestroyDecoder();
+ }
+
+ if (bFull) {
+ av_buffer_unref(&m_pDevCtx);
+ }
+
+ return S_OK;
+}
+
+// ILAVDecoder
+STDMETHODIMP CDecD3D11::InitAllocator(IMemAllocator **ppAlloc)
+{
+ HRESULT hr = S_OK;
+ if (m_bReadBackFallback)
+ return E_NOTIMPL;
+
+ if (m_pAllocator == nullptr)
+ {
+ m_pAllocator = new CD3D11SurfaceAllocator(this, &hr);
+ if (!m_pAllocator) {
+ return E_OUTOFMEMORY;
+ }
+ if (FAILED(hr)) {
+ SAFE_DELETE(m_pAllocator);
+ return hr;
+ }
+
+ // Hold a reference on the allocator
+ m_pAllocator->AddRef();
+ }
+
+ // return the proper interface
+ return m_pAllocator->QueryInterface(__uuidof(IMemAllocator), (void **)ppAlloc);
+}
+
+STDMETHODIMP CDecD3D11::PostConnect(IPin *pPin)
+{
+ DbgLog((LOG_TRACE, 10, L"CDecD3D11::PostConnect()"));
+ HRESULT hr = S_OK;
+
+ ID3D11DecoderConfiguration *pD3D11DecoderConfiguration = nullptr;
+ hr = pPin->QueryInterface(&pD3D11DecoderConfiguration);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"-> ID3D11DecoderConfiguration not available, using fallback mode"));
+ }
+
+ // Release old D3D resources, we're about to re-init
+ m_pCallback->ReleaseAllDXVAResources();
+
+ // free the decoder to force a re-init down the line
+ SafeRelease(&m_pDecoder);
+
+ // and the old device
+ av_buffer_unref(&m_pDevCtx);
+
+ // device id (hwcontext API wants a string)
+ UINT nDevice = pD3D11DecoderConfiguration ? pD3D11DecoderConfiguration->GetD3D11AdapterIndex() : 0;
+
+ for (;;)
+ {
+ char deviceId[34] = { 0 };
+ _itoa_s(nDevice, deviceId, 10);
+
+ // allocate device context
+ int ret = av_hwdevice_ctx_create(&m_pDevCtx, AV_HWDEVICE_TYPE_D3D11VA, deviceId, nullptr, 0);
+ if (ret < 0) {
+ // if the device failed, try with the default device
+ if (nDevice != 0)
+ {
+ nDevice = 0;
+ continue;
+ }
+
+ DbgLog((LOG_ERROR, 10, L"-> Failed to create D3D11 hardware context"));
+ goto fail;
+ }
+
+ break;
+ }
+
+ // check if the connection supports native mode
+ {
+ CMediaType mt = m_pCallback->GetOutputMediaType();
+ if ((m_SurfaceFormat == DXGI_FORMAT_NV12 && mt.subtype != MEDIASUBTYPE_NV12)
+ || (m_SurfaceFormat == DXGI_FORMAT_P010 && mt.subtype != MEDIASUBTYPE_P010)
+ || (m_SurfaceFormat == DXGI_FORMAT_P016 && mt.subtype != MEDIASUBTYPE_P016)) {
+ DbgLog((LOG_ERROR, 10, L"-> Connection is not the appropriate pixel format for D3D11 Native"));
+
+ m_bReadBackFallback = false;
+ SafeRelease(&pD3D11DecoderConfiguration);
+ }
+ }
+
+ // verify hardware support
+ {
+ GUID guidConversion = GUID_NULL;
+ hr = FindVideoServiceConversion(m_pAVCtx->codec_id, m_pAVCtx->profile, m_SurfaceFormat, &guidConversion);
+ if (FAILED(hr))
+ {
+ goto fail;
+ }
+
+ // get decoder configuration
+ D3D11_VIDEO_DECODER_DESC desc = { 0 };
+ desc.Guid = guidConversion;
+ desc.OutputFormat = m_SurfaceFormat;
+ desc.SampleWidth = m_pAVCtx->coded_width;
+ desc.SampleHeight = m_pAVCtx->coded_height;
+
+ D3D11_VIDEO_DECODER_CONFIG decoder_config = { 0 };
+ hr = FindDecoderConfiguration(&desc, &decoder_config);
+ if (FAILED(hr))
+ {
+ goto fail;
+ }
+ }
+
+ // Notice the connected pin that we're sending D3D11 textures
+ if (pD3D11DecoderConfiguration)
+ {
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+
+ hr = pD3D11DecoderConfiguration->ActivateD3D11Decoding(pDeviceContext->device, pDeviceContext->device_context, pDeviceContext->lock_ctx, 0);
+ SafeRelease(&pD3D11DecoderConfiguration);
+
+ m_bReadBackFallback = FAILED(hr);
+ }
+ else
+ {
+ m_bReadBackFallback = true;
+ }
+
+ return S_OK;
+
+fail:
+ SafeRelease(&pD3D11DecoderConfiguration);
+ return E_FAIL;
+}
+
+STDMETHODIMP CDecD3D11::InitDecoder(AVCodecID codec, const CMediaType *pmt)
+{
+ HRESULT hr = S_OK;
+ DbgLog((LOG_TRACE, 10, L"CDecD3D11::InitDecoder(): Initializing D3D11 decoder"));
+
+ // Destroy old decoder
+ DestroyDecoder(false);
+
+ // reset stream compatibility
+ m_bFailHWDecode = false;
+
+ // Initialize ffmpeg
+ hr = CDecAvcodec::InitDecoder(codec, pmt);
+ if (FAILED(hr))
+ return hr;
+
+ if (check_dxva_codec_profile(m_pAVCtx->codec_id, m_pAVCtx->pix_fmt, m_pAVCtx->profile, AV_PIX_FMT_D3D11))
+ {
+ DbgLog((LOG_TRACE, 10, L"-> Incompatible profile detected, falling back to software decoding"));
+ return E_FAIL;
+ }
+
+ // initialize surface format to ensure the default media type is set properly
+ bool bHighBitdepth = (m_pAVCtx->codec_id == AV_CODEC_ID_HEVC && (m_pAVCtx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 || m_pAVCtx->profile == FF_PROFILE_HEVC_MAIN_10))
+ || (m_pAVCtx->codec_id == AV_CODEC_ID_VP9 && (m_pAVCtx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 || m_pAVCtx->profile == FF_PROFILE_VP9_2));
+ if (bHighBitdepth)
+ m_SurfaceFormat = DXGI_FORMAT_P010;
+ else
+ m_SurfaceFormat = DXGI_FORMAT_NV12;
+
+ m_dwSurfaceWidth = dxva_align_dimensions(m_pAVCtx->codec_id, m_pAVCtx->coded_width);
+ m_dwSurfaceHeight = dxva_align_dimensions(m_pAVCtx->codec_id, m_pAVCtx->coded_height);
+
+ return S_OK;
+}
+
+HRESULT CDecD3D11::AdditionaDecoderInit()
+{
+ AVD3D11VAContext *ctx = av_d3d11va_alloc_context();
+
+ if (m_pDecoder) {
+ FillHWContext(ctx);
+ }
+
+ m_pAVCtx->thread_count = 1;
+ m_pAVCtx->hwaccel_context = ctx;
+ m_pAVCtx->get_format = get_d3d11_format;
+ m_pAVCtx->get_buffer2 = get_d3d11_buffer;
+ m_pAVCtx->opaque = this;
+
+ m_pAVCtx->slice_flags |= SLICE_FLAG_ALLOW_FIELD;
+
+ // disable error concealment in hwaccel mode, it doesn't work either way
+ m_pAVCtx->error_concealment = 0;
+ av_opt_set_int(m_pAVCtx, "enable_er", 0, AV_OPT_SEARCH_CHILDREN);
+
+ return S_OK;
+}
+
+STDMETHODIMP CDecD3D11::FillHWContext(AVD3D11VAContext *ctx)
+{
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+
+ ctx->decoder = m_pDecoder;
+ ctx->video_context = pDeviceContext->video_context;
+ ctx->cfg = &m_DecoderConfig;
+ ctx->surface_count = m_nOutputViews;
+ ctx->surface = m_pOutputViews;
+
+ ctx->context_mutex = pDeviceContext->lock_ctx;
+
+ ctx->workaround = 0;
+
+ return S_OK;
+}
+
+STDMETHODIMP_(long) CDecD3D11::GetBufferCount()
+{
+ long buffers = 0;
+
+ // Native decoding should use 16 buffers to enable seamless codec changes
+ if (!m_bReadBackFallback)
+ buffers = 16;
+ else {
+ // Buffers based on max ref frames
+ if (m_nCodecId == AV_CODEC_ID_H264 || m_nCodecId == AV_CODEC_ID_HEVC)
+ buffers = 16;
+ else
+ buffers = 2;
+ }
+
+ // 4 extra buffers for handling and safety
+ buffers += 4;
+
+ /*if (m_bReadBackFallback) {
+ buffers += m_DisplayDelay;
+ }*/
+ if (m_pCallback->GetDecodeFlags() & LAV_VIDEO_DEC_FLAG_DVD) {
+ buffers += 4;
+ }
+ return buffers;
+}
+
+HRESULT CDecD3D11::PostDecode()
+{
+ if (m_bFailHWDecode) {
+ DbgLog((LOG_TRACE, 10, L"::PostDecode(): HW Decoder failed, falling back to software decoding"));
+ return E_FAIL;
+ }
+ return S_OK;
+}
+
+enum AVPixelFormat CDecD3D11::get_d3d11_format(struct AVCodecContext *s, const enum AVPixelFormat * pix_fmts)
+{
+ CDecD3D11 *pDec = (CDecD3D11 *)s->opaque;
+ const enum AVPixelFormat *p;
+ for (p = pix_fmts; *p != -1; p++) {
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p);
+
+ if (!desc || !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
+ break;
+
+ if (*p == AV_PIX_FMT_D3D11)
+ {
+ HRESULT hr = pDec->ReInitD3D11Decoder(s);
+ if (FAILED(hr))
+ {
+ pDec->m_bFailHWDecode = TRUE;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return *p;
+}
+
+int CDecD3D11::get_d3d11_buffer(struct AVCodecContext *c, AVFrame *frame, int flags)
+{
+ CDecD3D11 *pDec = (CDecD3D11 *)c->opaque;
+ HRESULT hr = S_OK;
+
+ if (frame->format != AV_PIX_FMT_D3D11) {
+ DbgLog((LOG_ERROR, 10, L"D3D11 buffer request, but not D3D11 pixfmt"));
+ pDec->m_bFailHWDecode = TRUE;
+ return -1;
+ }
+
+ hr = pDec->ReInitD3D11Decoder(c);
+ if (FAILED(hr)) {
+ pDec->m_bFailHWDecode = TRUE;
+ return -1;
+ }
+
+ if (pDec->m_bReadBackFallback && pDec->m_pFramesCtx)
+ {
+ int ret = av_hwframe_get_buffer(pDec->m_pFramesCtx, frame, 0);
+ frame->width = c->coded_width;
+ frame->height = c->coded_height;
+ return ret;
+ }
+ else if (pDec->m_bReadBackFallback == false && pDec->m_pAllocator)
+ {
+ IMediaSample *pSample = nullptr;
+ hr = pDec->m_pAllocator->GetBuffer(&pSample, nullptr, nullptr, 0);
+ if (SUCCEEDED(hr))
+ {
+ CD3D11MediaSample *pD3D11Sample = dynamic_cast<CD3D11MediaSample *>(pSample);
+
+ // fill the frame from the sample, including a reference to the sample
+ pD3D11Sample->GetAVFrameBuffer(frame);
+
+ frame->width = c->coded_width;
+ frame->height = c->coded_height;
+
+ // the frame holds the sample now, can release the direct interface
+ pD3D11Sample->Release();
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+STDMETHODIMP CDecD3D11::ReInitD3D11Decoder(AVCodecContext *c)
+{
+ HRESULT hr = S_OK;
+
+ // Don't allow decoder creation during first init
+ if (m_bInInit)
+ return S_FALSE;
+
+ // sanity check that we have a device
+ if (m_pDevCtx == nullptr)
+ return E_FAIL;
+
+ // we need an allocator at this point
+ if (m_bReadBackFallback == false && m_pAllocator == nullptr)
+ return E_FAIL;
+
+ if (m_pDecoder == nullptr || m_dwSurfaceWidth != dxva_align_dimensions(c->codec_id, c->coded_width) || m_dwSurfaceHeight != dxva_align_dimensions(c->codec_id, c->coded_height) || m_DecodePixelFormat != c->sw_pix_fmt)
+ {
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+ DbgLog((LOG_TRACE, 10, L"No D3D11 Decoder or image dimensions changed -> Re-Allocating resources"));
+
+ // if we're not in readback mode, we need to flush all the frames
+ if (m_bReadBackFallback == false)
+ avcodec_flush_buffers(c);
+
+ pDeviceContext->lock(pDeviceContext->lock_ctx);
+ hr = CreateD3D11Decoder();
+ pDeviceContext->unlock(pDeviceContext->lock_ctx);
+ if (FAILED(hr))
+ return hr;
+
+ // Update the frames context in the allocator
+ if (m_bReadBackFallback == false)
+ {
+ // decommit the allocator
+ m_pAllocator->Decommit();
+
+ // verify we were able to decommit all its resources
+ if (m_pAllocator->DecommitInProgress()) {
+ DbgLog((LOG_TRACE, 10, L"WARNING! D3D11 Allocator is still busy, trying to flush downstream"));
+ m_pCallback->ReleaseAllDXVAResources();
+ m_pCallback->GetOutputPin()->GetConnected()->BeginFlush();
+ m_pCallback->GetOutputPin()->GetConnected()->EndFlush();
+ if (m_pAllocator->DecommitInProgress()) {
+ DbgLog((LOG_TRACE, 10, L"WARNING! Flush had no effect, decommit of the allocator still not complete"));
+ }
+ else {
+ DbgLog((LOG_TRACE, 10, L"Flush was successfull, decommit completed!"));
+ }
+ }
+
+ // re-commit it to update its frame reference
+ m_pAllocator->Commit();
+ }
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP CDecD3D11::FindVideoServiceConversion(AVCodecID codec, int profile, DXGI_FORMAT surface_format, GUID *input )
+{
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+ HRESULT hr = S_OK;
+
+ UINT nProfiles = pDeviceContext->video_device->GetVideoDecoderProfileCount();
+ GUID *guid_list = (GUID *)av_malloc_array(nProfiles, sizeof(*guid_list));
+
+ DbgLog((LOG_TRACE, 10, L"-> Enumerating supported D3D11 modes (count: %d)", nProfiles));
+ for (UINT i = 0; i < nProfiles; i++)
+ {
+ hr = pDeviceContext->video_device->GetVideoDecoderProfile(i, &guid_list[i]);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"Error retrieving decoder profile"));
+ av_free(guid_list);
+ return hr;
+ }
+
+#ifdef DEBUG
+ const dxva_mode_t *mode = get_dxva_mode_from_guid(&guid_list[i]);
+ if (mode) {
+ DbgLog((LOG_TRACE, 10, L" -> %S", mode->name));
+ }
+ else {
+ DbgLog((LOG_TRACE, 10, L" -> Unknown GUID (%s)", WStringFromGUID(guid_list[i]).c_str()));
+ }
+#endif
+ }
+
+ /* Iterate over our priority list */
+ for (unsigned i = 0; dxva_modes[i].name; i++) {
+ const dxva_mode_t *mode = &dxva_modes[i];
+ if (!check_dxva_mode_compatibility(mode, codec, profile))
+ continue;
+
+ BOOL supported = FALSE;
+ for (UINT g = 0; !supported && g < nProfiles; g++) {
+ supported = IsEqualGUID(*mode->guid, guid_list[g]);
+ }
+ if (!supported)
+ continue;
+
+ DbgLog((LOG_TRACE, 10, L"-> Trying to use '%S'", mode->name));
+ hr = pDeviceContext->video_device->CheckVideoDecoderFormat(mode->guid, surface_format, &supported);
+ if (SUCCEEDED(hr) && supported) {
+ *input = *mode->guid;
+
+ av_free(guid_list);
+ return S_OK;
+ }
+ }
+
+ av_free(guid_list);
+ return E_FAIL;
+}
+
+STDMETHODIMP CDecD3D11::FindDecoderConfiguration(const D3D11_VIDEO_DECODER_DESC *desc, D3D11_VIDEO_DECODER_CONFIG *pConfig)
+{
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+ HRESULT hr = S_OK;
+
+ UINT nConfig = 0;
+ hr = pDeviceContext->video_device->GetVideoDecoderConfigCount(desc, &nConfig);
+ if (FAILED(hr)) {
+ DbgLog((LOG_ERROR, 10, L"Unable to retreive decoder configuration count"));
+ return E_FAIL;
+ }
+
+ int best_score = 0;
+ D3D11_VIDEO_DECODER_CONFIG best_config;
+ for (UINT i = 0; i < nConfig; i++)
+ {
+ D3D11_VIDEO_DECODER_CONFIG config = { 0 };
+ hr = pDeviceContext->video_device->GetVideoDecoderConfig(desc, i, &config);
+ if (FAILED(hr))
+ continue;
+
+ int score;
+ if (config.ConfigBitstreamRaw == 1)
+ score = 1;
+ else if (m_pAVCtx->codec_id == AV_CODEC_ID_H264 && config.ConfigBitstreamRaw == 2)
+ score = 2;
+ else
+ continue;
+ if (IsEqualGUID(config.guidConfigBitstreamEncryption, DXVA2_NoEncrypt))
+ score += 16;
+ if (score > best_score) {
+ best_score = score;
+ best_config = config;
+ }
+ }
+
+ if (best_score <= 0) {
+ DbgLog((LOG_TRACE, 10, L"-> No matching configuration available"));
+ return E_FAIL;
+ }
+
+ *pConfig = best_config;
+ return S_OK;
+}
+
+static DXGI_FORMAT d3d11va_map_sw_to_hw_format(enum AVPixelFormat pix_fmt)
+{
+ switch (pix_fmt) {
+ case AV_PIX_FMT_YUV420P10:
+ case AV_PIX_FMT_P010: return DXGI_FORMAT_P010;
+ case AV_PIX_FMT_NV12:
+ default: return DXGI_FORMAT_NV12;
+ }
+}
+
+STDMETHODIMP CDecD3D11::CreateD3D11Decoder()
+{
+ HRESULT hr = S_OK;
+ AVD3D11VADeviceContext *pDeviceContext = (AVD3D11VADeviceContext *)((AVHWDeviceContext *)m_pDevCtx->data)->hwctx;
+
+ // release the old decoder, it needs to be re-created
+ SafeRelease(&m_pDecoder);
+
+ // find a decoder configuration
+ GUID profileGUID = GUID_NULL;
+ DXGI_FORMAT surface_format = d3d11va_map_sw_to_hw_format(m_pAVCtx->sw_pix_fmt);
+ hr = FindVideoServiceConversion(m_pAVCtx->codec_id, m_pAVCtx->profile, surface_format, &profileGUID);
+ if (FAILED(hr))
+ {
+ DbgLog((LOG_ERROR, 10, L"-> No video service profile found"));
+ return hr;
+ }
+
+ // get decoder configuration
+ D3D11_VIDEO_DECODER_DESC desc = { 0 };
+ desc.Guid = profileGUID;
+ desc.OutputFormat = surface_format;
+ desc.SampleWidth = m_pAVCtx->coded_width;
+ desc.SampleHeight = m_pAVCtx->coded_height;
+
+ D3D11_VIDEO_DECODER_CONFIG decoder_config = { 0 };
+ hr = FindDecoderConfiguration(&desc, &decoder_config);
+ if (FAILED(hr))
+ {
+ DbgLog((LOG_ERROR, 10, L"-> No valid video decoder configuration found"));
+ return hr;
+ }
+
+ m_DecoderConfig = decoder_config;
+
+ // update surface properties
+ m_dwSurfaceWidth = dxva_align_dimensions(m_pAVCtx->codec_id, m_pAVCtx->coded_width);
+ m_dwSurfaceHeight = dxva_align_dimensions(m_pAVCtx->codec_id, m_pAVCtx->coded_height);
+ m_DecodePixelFormat = m_pAVCtx->sw_pix_fmt;
+ m_SurfaceFormat = surface_format;
+
+ if (m_bReadBackFallback == false && m_pAllocator)
+ {
+ ALLOCATOR_PROPERTIES properties;
+ hr = m_pAllocator->GetProperties(&properties);
+ if (FAILED(hr))
+ return hr;
+
+ m_dwSurfaceCount = properties.cBuffers;
+ }
+ else
+ {
+ m_dwSurfaceCount = GetBufferCount();
+ }
+
+ // allocate a new frames context for the dimensions and format
+ hr = AllocateFramesContext(m_dwSurfaceWidth, m_dwSurfaceHeight, m_DecodePixelFormat, m_dwSurfaceCount, &m_pFramesCtx);
+ if (FAILED(hr))
+ {
+ DbgLog((LOG_ERROR, 10, L"-> Error allocating frames context"));
+ return hr;
+ }
+
+ // release any old output views and allocate memory for the new ones
+ if (m_pOutputViews)
+ {
+ for (int i = 0; i < m_nOutputViews; i++)
+ {
+ SafeRelease(&m_pOutputViews[i]);
+ }
+ av_freep(&m_pOutputViews);
+ }
+
+ m_pOutputViews = (ID3D11VideoDecoderOutputView **)av_mallocz_array(m_dwSurfaceCount, sizeof(*m_pOutputViews));
+ m_nOutputViews = m_dwSurfaceCount;
+
+ // allocate output views for the frames
+ AVD3D11VAFramesContext *pFramesContext = (AVD3D11VAFramesContext *)((AVHWFramesContext *)m_pFramesCtx->data)->hwctx;
+ for (int i = 0; i < m_nOutputViews; i++)
+ {
+ D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc = { 0 };
+ viewDesc.DecodeProfile = profileGUID;
+ viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
+ viewDesc.Texture2D.ArraySlice = i;
+
+ hr = pDeviceContext->video_device->CreateVideoDecoderOutputView(pFramesContext->texture, &viewDesc, &m_pOutputViews[i]);
+ if (FAILED(hr))
+ {
+ DbgLog((LOG_ERROR, 10, L"-> Failed to create video decoder output views"));
+ return E_FAIL;
+ }
+ }
+
+ // create the decoder
+ hr = pDeviceContext->video_device->CreateVideoDecoder(&desc, &decoder_config, &m_pDecoder);
+ if (FAILED(hr))
+ {
+ DbgLog((LOG_ERROR, 10, L"-> Failed to create video decoder object"));
+ return E_FAIL;
+ }
+
+ FillHWContext((AVD3D11VAContext *)m_pAVCtx->hwaccel_context);
+
+ return S_OK;
+}
+
+STDMETHODIMP CDecD3D11::AllocateFramesContext(int width, int height, AVPixelFormat format, int nSurfaces, AVBufferRef **ppFramesCtx)
+{
+ ASSERT(m_pAVCtx);
+ ASSERT(m_pDevCtx);
+ ASSERT(ppFramesCtx);
+
+ // unref any old buffer
+ av_buffer_unref(ppFramesCtx);
+
+ // allocate a new frames context for the device context
+ *ppFramesCtx = av_hwframe_ctx_alloc(m_pDevCtx);
+ if (*ppFramesCtx == nullptr)
+ return E_OUTOFMEMORY;
+
+ AVHWFramesContext *pFrames = (AVHWFramesContext *)(*ppFramesCtx)->data;
+ pFrames->format = AV_PIX_FMT_D3D11;
+ pFrames->sw_format = (format == AV_PIX_FMT_YUV420P10) ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12;
+ pFrames->width = width;
+ pFrames->height = height;
+ pFrames->initial_pool_size = nSurfaces;
+
+ AVD3D11VAFramesContext *pFramesHWContext = (AVD3D11VAFramesContext *)pFrames->hwctx;
+ pFramesHWContext->BindFlags |= D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE;
+ pFramesHWContext->MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
+
+ int ret = av_hwframe_ctx_init(*ppFramesCtx);
+ if (ret < 0)
+ {
+ av_buffer_unref(ppFramesCtx);
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDecD3D11::HandleDXVA2Frame(LAVFrame *pFrame)
+{
+ ASSERT(pFrame->format == LAVPixFmt_D3D11);
+
+ if (pFrame->flags & LAV_FRAME_FLAG_FLUSH) {
+ /*if (m_bReadBackFallback) {
+ FlushDisplayQueue(TRUE);
+ }*/
+ Deliver(pFrame);
+ return S_OK;
+ }
+
+ if (m_bReadBackFallback)
+ {
+ AVFrame *src = (AVFrame *)pFrame->priv_data;
+ AVFrame *dst = av_frame_alloc();
+
+ int ret = av_hwframe_transfer_data(dst, src, 0);
+ if (ret < 0)
+ {
+ ReleaseFrame(&pFrame);
+ av_frame_free(&dst);
+ return E_FAIL;
+ }
+
+ // free the source frame
+ av_frame_free(&src);
+
+ // and store the dst frame in LAVFrame
+ pFrame->priv_data = dst;
+ GetPixelFormat(&pFrame->format, &pFrame->bpp);
+
+ ASSERT((dst->format == AV_PIX_FMT_NV12 && pFrame->format == LAVPixFmt_NV12) || (dst->format == AV_PIX_FMT_P010 && pFrame->format == LAVPixFmt_P016));
+
+ for (int i = 0; i < 4; i++) {
+ pFrame->data[i] = dst->data[i];
+ pFrame->stride[i] = dst->linesize[i];
+ }
+
+ Deliver(pFrame);
+ }
+ else
+ {
+ AVFrame *pAVFrame = (AVFrame *)pFrame->priv_data;
+ pFrame->data[0] = pAVFrame->data[3];
+ pFrame->data[1] = pFrame->data[2] = pFrame->data[3] = nullptr;
+
+ GetPixelFormat(&pFrame->format, &pFrame->bpp);
+
+ Deliver(pFrame);
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP CDecD3D11::GetPixelFormat(LAVPixelFormat *pPix, int *pBpp)
+{
+ // Output is always NV12 or P010
+ if (pPix)
+ *pPix = m_bReadBackFallback == false ? LAVPixFmt_D3D11 : ((m_SurfaceFormat == DXGI_FORMAT_P010 || m_SurfaceFormat == DXGI_FORMAT_P016) ? LAVPixFmt_P016 : LAVPixFmt_NV12);
+
+ if (pBpp)
+ *pBpp = (m_SurfaceFormat == DXGI_FORMAT_P016) ? 16 : (m_SurfaceFormat == DXGI_FORMAT_P010 ? 10 : 8);
+
+ return S_OK;
+}
diff --git a/decoder/LAVVideo/decoders/d3d11va.h b/decoder/LAVVideo/decoders/d3d11va.h
new file mode 100644
index 00000000..dfaf2136
--- /dev/null
+++ b/decoder/LAVVideo/decoders/d3d11va.h
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2017 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
+#include "DecBase.h"
+#include "avcodec.h"
+
+#include <d3d11.h>
+#include <dxgi.h>
+
+#include "d3d11/D3D11SurfaceAllocator.h"
+
+extern "C" {
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_d3d11va.h"
+#include "libavcodec/d3d11va.h"
+}
+
+class CDecD3D11 : public CDecAvcodec
+{
+public:
+ CDecD3D11(void);
+ virtual ~CDecD3D11(void);
+
+ // ILAVDecoder
+ STDMETHODIMP InitDecoder(AVCodecID codec, const CMediaType *pmt);
+ STDMETHODIMP GetPixelFormat(LAVPixelFormat *pPix, int *pBpp);
+
+ STDMETHODIMP InitAllocator(IMemAllocator **ppAlloc);
+ STDMETHODIMP PostConnect(IPin *pPin);
+ STDMETHODIMP_(long) GetBufferCount();
+ STDMETHODIMP_(const WCHAR*) GetDecoderName() { return m_bReadBackFallback ? L"d3d11 cb" : L"d3d11 native"; }
+ STDMETHODIMP HasThreadSafeBuffers() { return S_FALSE; }
+
+protected:
+ HRESULT AdditionaDecoderInit();
+ HRESULT PostDecode();
+
+ HRESULT HandleDXVA2Frame(LAVFrame *pFrame);
+
+private:
+ STDMETHODIMP DestroyDecoder(bool bFull, bool bNoAVCodec = false);
+
+ STDMETHODIMP ReInitD3D11Decoder(AVCodecContext *c);
+
+ STDMETHODIMP CreateD3D11Decoder();
+ STDMETHODIMP AllocateFramesContext(int width, int height, AVPixelFormat format, int nSurfaces, AVBufferRef **pFramesCtx);
+
+ STDMETHODIMP FindVideoServiceConversion(AVCodecID codec, int profile, DXGI_FORMAT surface_format, GUID *input);
+ STDMETHODIMP FindDecoderConfiguration(const D3D11_VIDEO_DECODER_DESC *desc, D3D11_VIDEO_DECODER_CONFIG *pConfig);
+
+ STDMETHODIMP FillHWContext(AVD3D11VAContext *ctx);
+
+ static enum AVPixelFormat get_d3d11_format(struct AVCodecContext *s, const enum AVPixelFormat * pix_fmts);
+ static int get_d3d11_buffer(struct AVCodecContext *c, AVFrame *pic, int flags);
+
+private:
+ CD3D11SurfaceAllocator *m_pAllocator = nullptr;
+
+ AVBufferRef *m_pDevCtx = nullptr;
+ AVBufferRef *m_pFramesCtx = nullptr;
+
+ D3D11_VIDEO_DECODER_CONFIG m_DecoderConfig;
+ ID3D11VideoDecoder *m_pDecoder = nullptr;
+
+ int m_nOutputViews = 0;
+ ID3D11VideoDecoderOutputView **m_pOutputViews = nullptr;
+
+ DWORD m_dwSurfaceWidth = 0;
+ DWORD m_dwSurfaceHeight = 0;
+ DWORD m_dwSurfaceCount = 0;
+ AVPixelFormat m_DecodePixelFormat = AV_PIX_FMT_NONE;
+ DXGI_FORMAT m_SurfaceFormat = DXGI_FORMAT_UNKNOWN;
+
+ BOOL m_bReadBackFallback = FALSE;
+ BOOL m_bFailHWDecode = FALSE;
+
+ friend class CD3D11SurfaceAllocator;
+};
diff --git a/decoder/LAVVideo/decoders/pixfmt.cpp b/decoder/LAVVideo/decoders/pixfmt.cpp
index 9ac53e60..ce627c9f 100644
--- a/decoder/LAVVideo/decoders/pixfmt.cpp
+++ b/decoder/LAVVideo/decoders/pixfmt.cpp
@@ -170,7 +170,7 @@ HRESULT FreeLAVFrameBuffers(LAVFrame *pFrame)
HRESULT CopyLAVFrame(LAVFrame *pSrc, LAVFrame **ppDst)
{
- ASSERT(pSrc->format != LAVPixFmt_DXVA2);
+ ASSERT(pSrc->format != LAVPixFmt_DXVA2 && pSrc->format != LAVPixFmt_D3D11);
*ppDst = (LAVFrame *)CoTaskMemAlloc(sizeof(LAVFrame));
if (!*ppDst) return E_OUTOFMEMORY;
**ppDst = *pSrc;
diff --git a/decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp b/decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp
index 1d2c3a81..fe303f39 100644
--- a/decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp
+++ b/decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp
@@ -182,6 +182,10 @@ STDMETHODIMP CLAVSubtitleConsumer::ProcessFrame(LAVFrame *pFrame)
format = LAVPixFmt_NV12;
bpp = 8;
+ } else if (pFrame->format == LAVPixFmt_D3D11) {
+ // TODO D3D11
+ SafeRelease(&m_SubtitleFrame);
+ return E_FAIL;
} else {
if (!(pFrame->flags & LAV_FRAME_FLAG_BUFFER_MODIFY)) {
CopyLAVFrameInPlace(pFrame);