diff options
author | Hendrik Leppkes <h.leppkes@gmail.com> | 2017-07-05 18:33:14 +0300 |
---|---|---|
committer | Hendrik Leppkes <h.leppkes@gmail.com> | 2017-08-11 17:22:40 +0300 |
commit | 465f06e157379521bcda5a8220e658d9a5a41f14 (patch) | |
tree | c55eda096db625ca337907d75c743621f9dec0b8 /decoder | |
parent | cd936764aa3915c858c824cf48301202bb69126e (diff) |
Implement D3D11 hardware decoding mode
Diffstat (limited to 'decoder')
-rw-r--r-- | decoder/LAVVideo/DecodeManager.cpp | 4 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVPixFmtConverter.cpp | 6 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.cpp | 22 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.vcxproj | 5 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideo.vcxproj.filters | 21 | ||||
-rw-r--r-- | decoder/LAVVideo/LAVVideoSettings.h | 1 | ||||
-rw-r--r-- | decoder/LAVVideo/VideoSettingsProp.cpp | 2 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/ILAVDecoder.h | 2 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/avcodec.cpp | 5 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.cpp | 215 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/d3d11/D3D11SurfaceAllocator.h | 75 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/d3d11/ID3DVideoMemoryConfiguration.h | 48 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/d3d11va.cpp | 767 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/d3d11va.h | 95 | ||||
-rw-r--r-- | decoder/LAVVideo/decoders/pixfmt.cpp | 2 | ||||
-rw-r--r-- | decoder/LAVVideo/subtitles/LAVSubtitleConsumer.cpp | 4 |
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); |